Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[SymfonyCon Berlin] A year of Symfony

Sarah KHALIL
December 03, 2016

[SymfonyCon Berlin] A year of Symfony

Last year, Symfony 3 was out right before the SymfonyCon. A lot happened! 52 blog posts to help you keep up with all new things, 1200+ pull requests, 2 new versions out… Well I'm sure you missed something. Let's review what happened during last year: basically we'll see and/or discover nice new features that appeared since the last SymfonyCon.

Sarah KHALIL

December 03, 2016
Tweet

More Decks by Sarah KHALIL

Other Decks in Technology

Transcript

  1. 57 versions 2.6.x 1 version 2.7.x 14 versions 2.8.x 14

    versions 3.0.x 9 versions 3.1.x 10 versions 3.2.x 4 versions ➡ ➡ ➡ ➡ ➡ ➡ Releases
  2. New way of releasing Symfony • A new minor version

    every 6 months • in november and may, usually at the end of the month • A new major version every 2 years • released at the same time as the last minor version
  3. Getting started: all basics about developing apps with Symfony. Components:

    all about the standalone Symfony libraries. Bundles: commonly used bundles when developing Symfony apps. Reference: Form types, DI tags & Symfony configuration.
  4. Getting started: all basics about developing apps with Symfony. Guides

    and tutorials: Everything else. Components: all about the standalone Symfony libraries. Bundles: commonly used bundles when developing Symfony apps. Reference: Form types, DI tags & Symfony configuration.
  5. Deprecation helper improvements Detection of deprecation are done thanks to

    the PHPUnit bridge. <!-- phpunit.xml --> <phpunit> <!-- ... --> <php> <server name="KERNEL_DIR" value="app/" /> <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled" /> </php> </phpunit> … 3.1
  6. « disabled » PHPUnit won’t list the deprecations ; avoid

    your test suite failing. Deprecation helper improvements Detection of deprecation are done thanks to the PHPUnit bridge. <!-- phpunit.xml --> <phpunit> <!-- ... --> <php> <server name="KERNEL_DIR" value="app/" /> <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled" /> </php> </phpunit> … 3.1
  7. « /Passing callable strings .*/ » Will be matched as

    a regular expression against the deprecation message. « disabled » PHPUnit won’t list the deprecations ; avoid your test suite failing. Deprecation helper improvements Detection of deprecation are done thanks to the PHPUnit bridge. <!-- phpunit.xml --> <phpunit> <!-- ... --> <php> <server name="KERNEL_DIR" value="app/" /> <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled" /> </php> </phpunit> … 3.1
  8. « 10 » Test suite will fail if 10 or

    more deprecations are triggered. « /Passing callable strings .*/ » Will be matched as a regular expression against the deprecation message. « disabled » PHPUnit won’t list the deprecations ; avoid your test suite failing. Deprecation helper improvements Detection of deprecation are done thanks to the PHPUnit bridge. <!-- phpunit.xml --> <phpunit> <!-- ... --> <php> <server name="KERNEL_DIR" value="app/" /> <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled" /> </php> </phpunit> … 3.1
  9. • Implementation of the PSR-6 - Caching interface standard. •

    Cache arbitrary content in your application. • Symfony components use it to improve their performances. New component: Cache 3.1 class BlogController extends Controller { public function indexAction() { // create a new item and getting it from the cache $cachedCategories = $this->get('cache.app')->getItem('categories'); if (!$cachedCategories->isHit()) { $categories = ... // fetch categories from the database $cachedCategories->set($categories); $this->get('cache.app')->save($cachedCategories); } else { $categories = $cachedCategories->get(); } // ... } } Adapters for common backends Redis, APCu, Memcache…
  10. • Implementation of the PSR-6 - Caching interface standard. •

    Cache arbitrary content in your application. • Symfony components use it to improve their performances. New component: Cache 3.1 class BlogController extends Controller { public function indexAction() { // create a new item and getting it from the cache $cachedCategories = $this->get('cache.app')->getItem('categories'); if (!$cachedCategories->isHit()) { $categories = ... // fetch categories from the database $cachedCategories->set($categories); $this->get('cache.app')->save($cachedCategories); } else { $categories = $cachedCategories->get(); } // ... } } Adapters for common backends Redis, APCu, Memcache…
  11. YAML deprecation 3.1 framework: secret: %secret% framework: secret: '%secret%' parameters:

    my_object: '!php/object:O:27:"AppBundle\Service\MyService":1:{s:1:"b";s:3:"foo";}' parameters: my_object: '!!php/object:O:27:"AppBundle\Service\MyService":1:{s:1:"b";s:3:"foo";}'
  12. Process: Input stream 3.1 use Symfony\Component\Process\Process; $process = new Process('cat');

    $process->setInput('file.txt'); $process->run(); use Symfony\Component\Process\InputStream; $input = new InputStream(); $input->write('foo'); $process = new Process('my_script'); $process->setInput($input); $process-> start(); // ... read process output or do other things $input->write('bar'); // ... read process output or do other things $input->write('qux'); $input-> close(); before Cannot provide more input now
  13. Process: Output stream 3.1 Process::OUT Process::ERR or use Symfony\Component\Process\Process; $process

    = new Process('ls -lsa'); $process->start(); foreach ($process as $type => $data) { if ($process::OUT === $type) { echo $data."\n"; } else { echo "[ERR] ".$data."\n"; } } returns an iterator
  14. Process: Output stream 3.1 Process::OUT Process::ERR or use Symfony\Component\Process\Process; $process

    = new Process('ls -lsa'); $process->start(); foreach ($process as $type => $data) { if ($process::OUT === $type) { echo $data."\n"; } else { echo "[ERR] ".$data."\n"; } } returns an iterator
  15. 3.2

  16. use Symfony\Bundle\FrameworkBundle\Controller\Controller; class DefaultController extends Controller { public function downloadAction()

    { $pdfPath = $this->getParameter('dir.downloads').'/my_file.pdf'; return $this->file($pdfPath, ‘my_new_filename.pdf’, ResponseHeaderBag::DISPOSITION_INLINE); } } New file method helper protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) 3.2
  17. YAML supports PHP constants parameters: # this is considered a

    regular string foo: PHP_INT_MAX # this is considered a PHP constant bar: !php/const:PHP_INT_MAX 3.2
  18. Compiler pass improvements 1/2 // in compiler pass before 3.2

    $warmers = array(); foreach ($container->findTaggedServiceIds('kernel.cache_warmer') as $id => $attributes) { $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; $warmers[$priority][] = new Reference($id); } krsort($warmers); $warmers = call_user_func_array('array_merge', $warmers); // since 3.2 $warmers = $this->findAndSortTaggedServices('kernel.cache_warmer', $container); 3.2
  19. New form type: DateInterval 3.2 ->add('remindEvery', DateIntervalType::class, array( 'placeholder' =>

    array('years' => 'Years', 'months' => 'Months', 'days' => 'Days'), 'widget' => 'choice', // customize which text boxes are shown 'with_years' => true, 'with_months' => true, 'with_days' => true, 'with_hours' => false, ))
  20. Tagged cache $cache = new FilesystemAdapter(); $review = $cache->getItem('reviews-'.$reviewId); $review->set('...');

    $review->tag(['reviews', 'products', 'product-'.$productId]); $cache->save($review); //… // the HTML structure of reviews has changed: // invalidate all reviews $cache->invalidateTags('reviews'); // instead of $cache->deleteItem('reviews-'.$reviewId); 3.2
  21. Routing improvement 3.2 // generating a URL with a fragment

    $this->get('router')->generate('user_settings', ['_fragment' => 'password']); /settings#password // route definition /** * @Route("/settings", defaults={"_fragment" = "password"}, name="user_settings") */ public function settingsAction() { ... }
  22. YAML deprecations • No more duplicated keys otherwise you’ll get

    a ParseException 3.2 parameters: foo: bar parameters: key: 'aaa' # ... key: 'bbb' • Always put a space after colon otherwise you’ll get a ParseException
  23. Runtime Environment variable What’s new? • No longer obliged to

    prefix them by SYMFONY__ • The value is resolved at runtime! (the app has the updated value) 3.2 doctrine: dbal: # ... password: "%env(DB_PASSWORD)%" To set default value and avoid not set env variable: parameters: env(DB_PASSWORD): s3cr3t_p4ssw0rd
  24. Console improvements 3.2 $output->writeln('<fg=green;options=bold,underscore>Test</>'); Multiple text style options Hidden commands

    namespace AppBundle\Command; use Symfony\Component\Console\Command\Command; class FooCommand extends Command { protected function configure() { $this ->setName('app:foo') // ... ->setHidden(true) ; } }
  25. Serializer: CSV encoder 3.2 // instantiation, when using it inside

    the Symfony framework $serializer = $container->get('serializer'); $data = [ 'foo' => 'aaa', 'bar' => [ ['id' => 111, 1 => 'bbb'], ['lorem' => 'ipsum'], ] ]; file_put_contents( 'data.csv', $container->get('serializer')->encode($data, 'csv') ); // decoding CSV contents $data = $serializer->decode(file_get_contents('data.csv'), 'csv');
  26. Serializer: YAML encoder 3.2 $obj = new \stdClass(); $obj->bar =

    2; $data = $this->container->get('serializer')->encode( ['foo' => $obj], 'yaml' // these are the default values applied by the encoder ['yaml_inline' => 1, 'yaml_indent' => 4, 'yaml_flags' => 0] ); // $data = ' foo: !php/object:O:8:\"stdClass\":1:{s:3:\"bar\";i:2;}\n'; $data = $this->container->get('serializer')->decode( 'foo: !php/object:O:8:"stdClass":1:{s:3:"bar";i:2;}', 'yaml' ); // $data = ['foo' => $obj];
  27. Translation improvements // Before $this->get('translator') ->transChoice('1 apple|%count% apples', 7, ['%count%'

    => 7]); // After $this->get('translator') ->transChoice('1 apple|%count% apples', 7); 3.2 ./bin/console translation:update en --force --domain=admin
  28. Unicode routing support UTF-8 characters in route paths and requirements

    /** * @Route( * "/category/{name}", * "requirements" = { "name": "䴰ᬨ|بيحرت" } * "options" = { "utf8": true } * ) */ public function categoryAction($name) { // ... } /category/䴰ᬨ, /category/بيحرت 3.2
  29. Unicode routing support UTF-8 characters in route paths and requirements

    /** * @Route( * "/category/{name}", * "requirements" = { "name": "䴰ᬨ|بيحرت" } * "options" = { "utf8": true } * ) */ public function categoryAction($name) { // ... } /category/䴰ᬨ, /category/بيحرت not needed before 4.0 is able to detect UTF-8 3.2
  30. • Implementation of the petri net (Transitions, places…) • Supports

    state machine New component: Workflow https://speakerdeck.com/lyrixx/le-reveil-du-workflow 3.2
  31. • Implementation of the petri net (Transitions, places…) • Supports

    state machine New component: Workflow https://speakerdeck.com/lyrixx/le-reveil-du-workflow https://speakerdeck.com/tucksaun/symfonylive-london-2016-the-workflow-awakens 3.2
  32. • Implementation of the petri net (Transitions, places…) • Supports

    state machine New component: Workflow https://speakerdeck.com/lyrixx/le-reveil-du-workflow https://speakerdeck.com/tucksaun/symfonylive-london-2016-the-workflow-awakens https://speakerdeck.com/saro0h/openclassrooms-workflow-component 3.2
  33. Better support for one command applications 3.2 use Symfony\Component\Console\Application; $command

    = new \FooCommand(); $application = new Application(); $application->add($command); // the second boolean argument tells if this is a single-command app $application->setDefaultCommand($command->getName(), true); // this now executes the 'FooCommand' without passing its name $application->run();
  34. Simpler command testing 3.2 use Symfony\Component\Console\Tester\CommandTester; $commandTester = new CommandTester($command);

    $helper = $command->getHelper('question'); $helper->setInputStream($this->getInputStream("123\nfoo\nbar\n")); protected function getInputStream($input) { $stream = fopen('php://memory', 'r+', false); fputs($stream, $input); rewind($stream); return $stream; } use Symfony\Component\Console\Tester\CommandTester; $commandTester = new CommandTester($command); $commandTester->setInputs(['123', 'foo', 'bar']);