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
210
Symfony Internals
meandmymonkey
3
950
Divide and Conquer
meandmymonkey
1
740
Deptrac - Keep Your Architecture Clean
meandmymonkey
0
820
Introduction to Docker at PHPBenelux2015
meandmymonkey
3
930
Best Practices in Symfony2
meandmymonkey
0
540
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
OSもどきOS
arkw
0
590
Inside Stream API
skrb
1
770
IBM Bobを活用したレガシーアプリの最新化
oniak3ibm
PRO
1
210
Claspは野良GASの夢をみるか
takter00
0
210
Oxlintのカスタムルールの現況
syumai
6
1.2k
脅威をエンジニアリングの糧にして――現場編 / Turning Threats into Engineering Fuel — Field Edition
nrslib
0
300
エンジニア向け会社紹介/Findy Company Profile
findyinc
6
350k
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
12
4.4k
dRuby over BLE
makicamel
2
390
1B+ /day規模のログを管理する技術
broadleaf
0
110
TypeScript+Orvalで実現する型安全かつ堅牢でスケーラブルなマルチチャネル通知基盤 / TSKaigi Night talks ~after conference~
d0riven
0
360
AIを活用したE2Eテスト実装効率化のあゆみ / ebisu-mobile-14-kotetu
kotetuco
0
130
Featured
See All Featured
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
370
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
190
Color Theory Basics | Prateek | Gurzu
gurzu
0
370
Darren the Foodie - Storyboard
khoart
PRO
3
3.4k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.7k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.3k
Speed Design
sergeychernyshev
33
1.9k
Rails Girls Zürich Keynote
gr2m
96
14k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.8k
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
540
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.2k
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