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
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
830
Introduction to Docker at PHPBenelux2015
meandmymonkey
3
940
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.8k
Other Decks in Programming
See All in Programming
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
310
ここ半年くらいでAIに作らせたR用ツール
eitsupi
0
360
2年かけて Deno に DOMMatrix を実装した話 / How I implemented DOMMatrix in Deno over two years
petamoriken
0
200
PHP初心者セッション2026 〜生成AIでは見えない裏側を知る:今だからLAMPを通して仕組みを学ぶ〜
kashioka
0
820
VibeCodingからAgenticWorkflowへ
starfish719
0
220
GDG Korea Android: 2026 I/O Extended ~ What's new in Android development tools
pluu
0
210
AI時代、エンジニアはどう育つのか -未経験エンジニアの成長を間近で見て考えたこと-
thasu0123
0
210
ソフトウェア設計に溶けるインフラ ― AWS CDK のインフラ認識論
konokenj
3
750
生成AIで帳票OCRが「簡単に」作れる時代になった?
kon_shou
0
420
属人化した知識を、 AIが辿れる地図にする
pkshadeck
PRO
1
140
React本体のコードリーディング
high_g_engineer
1
130
「寝てても仕事が進む」Claude Codeで組む第二の脳
tomoyafujita2016
0
290
Featured
See All Featured
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
340
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Marketing to machines
jonoalderson
1
5.6k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
64
56k
HDC tutorial
michielstock
2
780
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
630
BBQ
matthewcrist
89
10k
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
190
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
400
Rails Girls Zürich Keynote
gr2m
96
14k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.8k
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