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
840
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
そこに3びきプロダクトがいるじゃろう——生成AI時代における“価値が届かない理由”の構造
kosuket
0
440
夏だ!祭りだ!祭りとはドメインモデリングでは?
ryugen04
0
190
Laravel Boostに学ぶ、AIにPHPを書かせる技術 〜OSSの実装から蒸留するエージェント制御の王道〜
kentaroutakeda
3
670
その節約、円になってますか?
isamumumu
1
710
React本体のコードリーディング
high_g_engineer
1
140
ここ半年くらいでAIに作らせたR用ツール
eitsupi
0
370
作るコストが小さくなった時代 幸せに働くために改めて考えたいこと 〜エンジニアとして価値を出し続けるために注視している二分野〜
yuppeeng
0
190
Claude CodeとAgentCore Gatewayを繋ぐ際の認証認可 / Authentication and authorization when connecting Claude Code with AgentCore Gateway
har1101
2
260
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
540
S3 を使うアプリケーションをローカル完結で動かすことに全力を注いでみた / Running S3 Apps Offline
contour_gara
0
400
壊れたパーサから始める関数型設計と構成的なパーサ #fp_matsuri
raiga0310
2
450
仕様書を書く前にハーネスを作る - Agent Native開発は「探索を速く、判定を固く」
gotalab555
4
1.6k
Featured
See All Featured
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.1k
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7.8k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
45k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
Discover your Explorer Soul
emna__ayadi
2
1.2k
Mobile First: as difficult as doing things right
swwweet
225
10k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
370
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.1k
Color Theory Basics | Prateek | Gurzu
gurzu
0
410
YesSQL, Process and Tooling at Scale
rocio
174
15k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
410
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
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