Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
What Symfony Components can Do for You
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Andreas Hucks
May 15, 2013
Programming
1.1k
10
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
What Symfony Components can Do for You
Andreas Hucks
May 15, 2013
More Decks by Andreas Hucks
See All by Andreas Hucks
Divide and Conquer (LonghornPHP 2019)
meandmymonkey
0
220
Symfony Internals
meandmymonkey
3
960
Divide and Conquer
meandmymonkey
1
750
Deptrac - Keep Your Architecture Clean
meandmymonkey
0
850
Introduction to Docker at PHPBenelux2015
meandmymonkey
3
950
Best Practices in Symfony2
meandmymonkey
0
550
Introduction to Docker at PHPNW2014
meandmymonkey
4
440
O(ops), Authentication!
meandmymonkey
4
1k
Best Practices in Symfony2
meandmymonkey
15
1.9k
Other Decks in Programming
See All in Programming
Go 1.27 における memory allocation の高速化
andpad
0
350
30年振りにコンパイラの定数整数除算を改善した
herumi
9
4.3k
片田舎のおっさん、 Swift Buildのダイアモンド問題解決の不具合修正PRを出すが、解決方法がキャッシュをしないようにすることであり、ビルド時間が伸びると言われてマージされないので高速化もする/swiftbuild
yimajo
0
320
typoなんかねぇよ
raspython3
0
610
AWS Transform Customによる Spring Boot 2.xから4.xへのVerUp
satoshi256kbyte
2
110
Flow は今どうなっているか
mizdra
PRO
0
760
Webエンジニアなのにブラウザの仕組みがわからないので、Pythonで自作してみた
tatsuki12
4
1.1k
Hello, Hiroshima Geospatial Data! — Exploring DoboX with Python
ra0kley
0
110
Press start. Python's next generation.
willingc
PRO
3
260
ソフトウェアエンジニアにとっての生成AI - 特性を知って使い倒す / generative ai for software enginner
kishida
7
2.2k
引き算の組織 ― アウトカムとAIに全振りするために辞めたこと ― / Organization by Subtraction
hirokiyamamoto14
PRO
0
310
[PyCon KR 2026] More Variants, More Diversity for AI Accelerators
achimnol
0
120
Featured
See All Featured
Amusing Abliteration
ianozsvald
1
270
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
2
3.8k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
480
Tell your own story through comics
letsgokoyo
1
1k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.8k
Paper Plane
katiecoart
PRO
2
53k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
360
Embracing the Ebb and Flow
colly
88
5.1k
BBQ
matthewcrist
89
10k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.4k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
820
Transcript
What Symfony Components can do for you. php[tek] 2013 Chicago,
May 15th Andreas Hucks
@meandmymonkey Andreas Hucks Trainer & Consultant at SensioLabs Deutschland
Symfony 2.0 Is a Full Stack Framework ...
None
... not only.
Symfony2 is a reusable set of standalone, decoupled, and cohesive
PHP 5.3 components that solve common web development problems.
Stuff built using SF2 Components (to varying degrees) • Symfony2
(duh) • Drupal 8 • Silex • Laravel • PPI • PHPUnit • Composer • ... I probably forgot something important
None
if ($_GET['action'] == 'close') { $query =
'UPDATE todo SET is_done = 1 WHERE id = '. mysql_real_escape_string($_GET['id']); mysql_query($query, $conn) or die('Unable to update existing task : '. mysql_error()); header('Location: /app.php/'); } $result = mysql_query('SELECT COUNT(*) FROM todo', $conn); $count = current(mysql_fetch_row($result)); $result = mysql_query('SELECT * FROM todo', $conn) ?> <table> <?php while ($todo = mysql_fetch_assoc($result)) { echo '<tr>'; echo ' <td class="center">'. $todo['id'] .'</td>'; echo ' <td><a href="/app.php/show?id='. $todo['id'] .'">'. $todo['title'] .'</a></td>'; echo ' <td class="center">'; 4.0 TM
None
None
None
HttpFoundation
HttpFoundation • OOP Interface for HTTP • No more Superglobals
• Helper Methods
Legacy Wrapper public function execute($file, Request $request) {
$file = $this-‐>basePath . $file; if (!is_file($file) && someSanityCheck($file)) { throw new \Exception('Invalid controller.'); } extract($this-‐>context); ob_start(); require_once $this-‐>basePath . $file; return new Response(ob_get_clean()); }
web/app.php use Legacy\Wrapper; use Symfony\Component\Debug\Debug; use Symfony\Component\HttpFoundation\Request; $wrapper = new
Wrapper(__DIR__ . '/../legacy'); $request = Request::createFromGlobals(); $response = $wrapper-‐>execute( $request-‐>getPathInfo(), $request ); $response-‐>send();
Testability & Compatibility $request = Request::createFromGlobals(); $response = doSomethingToGenerateResponse($request); $response-‐>send();
None
Wrap Up • Starting point for refactoring: • Isolated legacy
code • Can now be integrated into new app • Testable!
None
None
Debug
Debug • Error Handler • Exception Handler • Error Logging
(optionally)
Error Handler ErrorHandler::register($level = null); ErrorHandler::setLogger(LoggerInterface $logger = null);
Exception Handler ExceptionHandler::register($debug = true);
None
Debug Debug::enable($level = null);
None
None
Routing
Routing • Define routes as patterns • Assign attributes •
Match an incoming URI to a route • Generate an URI from a route object
Defining a Route $routeHome = new Route('/index.php'); $routeHome-‐>setDefault(
'_controller', function(Request $request) use ($wrapper) { return $wrapper-‐>execute('/index.php', $request); } );
Matching $routes = new RouteCollection(); $routes-‐>add('list', $routeHome); // ... $context
= new RequestContext(); $context-‐>fromRequest($request); $matcher = new UrlMatcher($routes, $context); $parameters = $matcher-‐>match($request-‐>getPathInfo());
Parameters? $routeHome-‐>setDefault('_controller', 'MyController'); $routeHome-‐>setDefault('page', 1); $routes-‐>add('list', $routeHome); array (
'_controller' => 'MyController', '_route' => 'list', 'page' => 1 )
Determine Controller $parameters = $matcher-‐>match($request-‐>getPathInfo()); $response = $parameters['_controller']($request); $response-‐>send();
None
None
None
Templating
• Simple, extensible templating engine • PHP! • Escaping, Inheritance
• Generic Interface to allow for easy engine replacement (Twig!)
<?php include 'config.php'; include
'header.php'; ?> <table> [...] / / here be dragons </table> <?php include 'footer.php' ?> 4.0 TM
layout.php [...] <div id="content"> <?php $view['slots']-‐>output('content'); ?>
</div> [...]
list.php <?php $view-‐>extend('layout.php') ?> <?php $view['slots']-‐>start('content') ?>
[...] <?php foreach ($tasks as $task): ?> <tr> <td><?php echo $task['title']; ?></td>/td> [...] </tr> <?php endforeach; ?> [...] <?php $view['slots']-‐>stop() ?>
Rendering $templating = new PhpEngine( new TemplateNameParser(),
new FilesystemLoader( array(__DIR__ . '/../templates/%name%') ) ); $html = $templating-‐>render( 'list.php', array( 'tasks' => $tasks ) );
Inside the Controller public function listAction(Request $request) {
// load tasks from db return new Response( $this-‐>templating-‐>render( 'list.php', array( 'urlGenerator' => $router-‐>getGenerator(), 'tasks' => $tasks ) ) ); }
Generating URLs [...] <div id="content"> <h1>
<a href= "<?php $urlGenerator-‐>generate('list'); ?>"> My Todo List </a> </h1> [...] </div> [...]
None
None
The all-in-one Router $context = new RequestContext(); $context-‐>fromRequest($request); $locator =
new FileLocator(__DIR__ . '/../config'); $router = new Router( new YamlFileLoader($locator), 'routing.yml', array( 'cache_dir' => __DIR__ . '/../cache' ), $context );
YAML Configuration list: pattern: /
methods: GET defaults: { _controller: list } show: pattern: /{id} methods: GET defaults: { _controller: show } create: pattern: / methods: POST defaults: { _controller: create }
Matcher and Generator $urlGenerator = $router-‐>getGenerator(); $matcher = $router-‐>getMatcher();
Call the (new) Controller $controller = // create controller instance
$parameters = $matcher-‐>match($request-‐>getPathInfo()); $response = call_user_func( array( $controller, $parameters['_controller'] . 'Action' ), $request ); $response-‐>send();
None
None
HttpKernel
HttpKernel • Provides a predefined workflow to convert a Request
into a Response • Events to hook into • Error Logging (optionally)
Let the kernel handle it. $dispatcher = new EventDispatcher(); $dispatcher-‐>addSubscriber(
new RouterListener($router-‐>getMatcher()) ); $resolver = new MyControllerResolver($controller); $kernel = new HttpKernel($dispatcher, $resolver); $request = Request::createFromGlobals(); $response = $kernel-‐>handle($request); $response-‐>send();
Controller Resolver? interface ControllerResolverInterface { public function getController(Request
$request); public function getArguments(Request $request, $controller); }
None
None
A look back $request = Request::createFromGlobals(); $response = $kernel-‐>handle($request);
BrowserKit & CssSelector
Base Test use Symfony\Component\HttpKernel\Client; class FunctionalTestCase extends \PHPUnit_Framework_TestCase {
protected static function createClient() { $kernel = // somehow create a kernel return new Client($kernel); } }
A Test Case class IndexPageTest extends FunctionalTestCase {
public function testList() { $client = static::createClient(); $crawler = $client-‐>request('GET', '/'); $this-‐>assertEquals(200, $client-‐>getResponse()-‐>getStatusCode()); $this-‐>assertCount(1, $crawler-‐>filter('h1:contains("My Todos List")')); } }
Testing Forms $form = $crawler-‐>selectButton('send')-‐>form(); $client-‐>submit($form, array('title' => 'MyTask')); $this-‐>assertEquals(302,
$client-‐>getResponse()-‐>getStatusCode()); $crawler = $client-‐>followRedirect(); $this-‐>assertCount(1, $crawler-‐>filter(sprintf('a:contains("%s")', 'MyTask')));
None
None
Config & Yaml
Config • Locating, Loading, Caching of config files • Validation
of config files • Merging of cascading config sets
Loading Configuration Data $configPath = __DIR__ . '/../config/config.yml'; $config =
Yaml::parse($configPath);
Loading Configuration Data $cachePath = __DIR__ . '/../cache/config.php'; $configPath =
__DIR__ . '/../config/config.yml'; $configCache = new ConfigCache($cachePath, true); if (!$configCache-‐>isFresh()) { $resource = new FileResource($configPath); $code = '<?php return ' . var_export(Yaml::parse($configPath), true) . ';'; $configCache-‐>write($code, array($resource)); } $config = require $cachePath;
None
Console
Console • Easy setup for CLI scripts • Output formatting,
help system • Interactive dialogs
Commands use Symfony\Component\Console\Command\Command; class AddTaskCommand extends Command {
public function configure() { $this-‐>setName('todo:add'); $this-‐>setDescription('Add a new task to your todo list'); $this-‐>addArgument('title', InputArgument::OPTIONAL, 'The task title'); } // ... }
The Application use Symfony\Component\Console\Application; $db = // create PDO instance
$app = new Application('Todo List Helpers'); $app-‐>add(new AddTaskCommand($db)); $app-‐>add(new ExpireTasksCommand($db)); $app-‐>run();
None
None
Execution public function execute(InputInterface $input, OutputInterface $output) {
$dialog = $this-‐>getHelperSet()-‐>get('dialog'); $title = $dialog-‐>ask( $output, '<question>What do you have to do?</question> ' ); if ($title) { // do stuff $output-‐>writeln('<info>Task created.</info>'); } else { $output-‐>writeln('<error>No input given!</error>'); } }
None
None
What else is there? • Form • Security • Validator
• Event Dispatcher • Finder • Process • PropertyAccess • OptionsResolver • ...
Go forth and learn! http://goo.gl/a0bCJ
Thanks! Questions? Please give feedback: http://goo.gl/IMK9n