Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
What Symfony Components can Do for You
Search
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
230
Symfony Internals
meandmymonkey
3
970
Divide and Conquer
meandmymonkey
1
770
Deptrac - Keep Your Architecture Clean
meandmymonkey
0
870
Introduction to Docker at PHPBenelux2015
meandmymonkey
3
960
Best Practices in Symfony2
meandmymonkey
0
560
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
FactoryBotをやめればRSpecは速くなるのか?
yuta_onishi_97
0
1.1k
PyConTW 2026 - AI Coding 不只是工具升級, 區域醫院醫療資訊系統協作開發與資訊團隊轉型實踐
cclai999
0
160
우리의 Nitro Modules는 당신의 Turbo Modules보다 아름답다
l2hyunwoo
0
190
Groovy as a human and AI friendly programming language
paulk
0
110
全ての永続トークンを、生まれる前に消し去りたい!
sylph01
2
1.1k
エージェントを使い続けるためのメタワーク / Meta-work for keeping using the agent
rkaga
6
1.8k
Rでドレミ/Do-Re-Mi_with_R
florets1
0
130
残高管理から台帳サービスへの進化
artoy
1
210
Optunaを作ったときにやりたかったこと、その後 (2026/10/16, Optuna Meetup #3)
iwiwi
3
1.2k
あなたの await、OS まで届いてる? strace で覗く JavaScript の非同期
hasutosasaki
1
1.3k
AIに “答え“を書かせるのを やめてみた
___akari11
0
130
コードレビューのボトルネックを"する側"と"される側"の両面から解消する
yub0n
2
1.7k
Featured
See All Featured
Fireside Chat
paigeccino
43
4.1k
Optimising Largest Contentful Paint
csswizardry
37
4k
A Modern Web Designer's Workflow
chriscoyier
699
190k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
640
Raft: Consensus for Rubyists
vanstee
142
7.8k
Kristin Tynski - Automating Marketing Tasks With AI
techseoconnect
PRO
0
550
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
3.2k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
1k
It's Worth the Effort
3n
188
29k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
232
55k
Fashionably flexible responsive web design (full day workshop)
malarkey
409
67k
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