$30 off During Our Annual Pro Sale. View Details »

Symfony 5.2

Fabien Potencier
September 23, 2020

Symfony 5.2

What's new in Symfony 5.2

Fabien Potencier

September 23, 2020
Tweet

More Decks by Fabien Potencier

Other Decks in Technology

Transcript

  1. Symfony 5.2
    Fabien Potencier
    @fabpot
    Icons made by Eucalyp from www.flaticon.com

    View Slide

  2. Console
    240M downloads

    View Slide

  3. Console Colors
    True color support via Hex CSS colors
    https://github.com/symfony/symfony/pull/36802
    $color = new Color('black', 'white');
    echo $color->apply("foo");
    $color = new Color('#000000', '#ffffff');

    View Slide

  4. private function rainbowColor(): Color
    {
    ++$this->color;
    if (256 === $this->color) {
    $this->color = 1;
    }
    $h = (int) ($this->color / 43);
    $f = (int) ($this->color - 43 * $h);
    $t = (int) ($f * 255 / 43);
    $q = 255 - $t;
    if ($h == 0) {
    return new Color('', sprintf('#FF%02x00', $t));
    } elseif ($h == 1) {
    return new Color('', sprintf('#%02xFF00', $q));
    } elseif ($h == 2) {
    return new Color('', sprintf('#00FF%02x', $t));
    } elseif ($h == 3) {
    return new Color('', sprintf('#00%02xFF', $q));
    } elseif ($h == 4) {
    return new Color('', sprintf('#%02x00FF', $t));
    } elseif ($h == 5) {
    return new Color('', sprintf('#FF00%02x', $q));
    }
    }
    Console Rainbows
    https://github.com/symfony/symfony/pull/36802
    $output->write($this->rainbowColor()->apply(' '));

    View Slide

  5. https://symfony.com/blog/new-in-symfony-5-1-cursor-control
    namespace App\Command;
    use Symfony\Component\Console\Command\Command;
    use Symfony\Component\Console\Cursor;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    class SomeCommand extends Command
    {
    protected static $defaultName = 'app:some-command';
    protected function execute(InputInterface $input, OutputInterface $output)
    {
    // ...
    $cursor = new Cursor($output);
    // argument for left/right is "number of columns" (1 by default)
    // argument for top/bottom is "number of rows" (1 by default)
    $cursor->moveUp(2)->moveRight();
    $cursor->moveDown();
    // move to an arbitrary (column, row) position
    $cursor->moveToPosition(7, 15);
    // you can show/hide the cursor, save/restore its position, etc.
    $cursor->savePosition()->hide();
    }
    }

    View Slide

  6. Snake
    a Symfony
    Console
    Game
    https://github.com/dbu/snake-bundle

    View Slide

  7. Default Command vs Single Command Applications
    #!/usr/bin/env php
    use Symfony\Component\Console\SingleCommandApplication;
    require __DIR__.'/vendor/autoload.php';
    (new SingleCommandApplication())
    ->setCode(
    function (InputInterface $input, OutputInterface $output) {
    // add here the code of your console command...
    }
    )
    ->run();
    #!/usr/bin/env php
    use Dbu\SnakeBundle\Command\SnakeCommand;
    use Symfony\Component\Console\Application;
    require __DIR__.'/vendor/autoload.php';
    $app = new Application();
    $app->add(new SnakeCommand());
    $app->setDefaultCommand('game:snake');
    $app->run();
    5.1

    View Slide

  8. https://github.com/fabpot/snake-bundle/tree/rainbow-time

    View Slide

  9. Console
    Signals
    $game = new Game(80, 24);
    $app = new Application();
    $app->add(new SnakeCommand($game));
    $app->setDefaultCommand('game:snake');
    $app->getSignalRegistry()->register(SIGUSR1, function ($signal) use ($game) {
    $game->cheat();
    });
    $app->run();
    https://github.com/symfony/symfony/pull/33729
    https://github.com/symfony/symfony/pull/37827
    pkill -USR1 -f "php ./snake"
    https://github.com/fabpot/snake-bundle/tree/rainbow-time

    View Slide

  10. Translation
    Created
    10 years ago

    View Slide

  11. $invitation = '{organizer_gender, select,
    female {{organizer_name} has invited you for her party!}
    male {{organizer_name} has invited you for his party!}
    other {{organizer_name} have invited you for their party!}
    }';
    // prints "Ryan has invited you for his party!"
    echo $translator->trans($invitation, [
    'organizer_name' => 'Ryan',
    'organizer_gender' => 'male',
    ]);
    Translation: Standard ICU message format
    https://github.com/symfony/symfony/pull/37371

    View Slide

  12. Translation: Pseudo localization translator
    https://github.com/symfony/symfony/pull/36016

    View Slide

  13. $message = new Translatable('Symfony is great!');
    $message = t('Symfony is great!');
    Translatable::trans($translator, $message);
    Translation: Translatable objects
    https://github.com/symfony/symfony/pull/37670

    View Slide

  14. Form
    Mapping data using callback functions
    https://github.com/symfony/symfony/pull/37968
    $builder->add('name', TextType::class, [
    'getter' => fn(Person $person, FormInterface $form): string => $person->firstName().' '.$person->lastName();
    'setter' => fn(Person &$person, ?string $name, FormInterface $form): void => $person->rename($name);
    ]);

    View Slide

  15. HttpClient

    View Slide

  16. HttpClient
    RetryableHttpClient
    use Symfony\Component\HttpClient\RetryableHttpClient;
    $client = new RetryableHttpClient(HttpClient::create());
    framework:
    http_client:
    retry_failed:
    # backoff_service: app.custom_backoff
    # decider_service: app.custom_decider
    http_codes: [429, 500]
    max_retries: 2
    delay: 1000
    multiplier: 3
    max_delay: 500
    https://github.com/symfony/symfony/pull/38182

    View Slide

  17. HttpClient
    EventSourceHttpClient
    https://github.com/symfony/symfony/pull/36692

    View Slide

  18. Experimental Components
    get better!

    View Slide

  19. Uid: Better framework integration
    https://symfony.com/components/Uid
    Support in Serializer (via a dedicated Normalizer) - Just works
    Uid Doctrine types and generators
    Help needed - https://github.com/symfony/symfony/issues/36102
    Uid constraint and more
    5.1

    View Slide

  20. Notifier: Still experimental
    We need your feedback!

    View Slide

  21. Notifier: Many new providers
    5.2
    all 5.1 +
    Esendex
    GoogleChat
    Infobip
    LinkedIn
    Mobyt
    Smsapi
    Zulip
    5.1
    all 5.0 +
    Firebase
    FreeMobile
    Mattermost
    OvhCloud
    RocketChat
    Sinch
    5.0
    Nexmo
    Slack
    Telegram
    Twilio

    View Slide

  22. Providers are now more widespread
    Notifier
    Esendex *
    Firebase
    FreeMobile
    GoogleChat *
    Infobip *
    LinkedIn *
    Mattermost
    Mobyt *
    Nexmo
    OvhCloud
    RocketChat
    Sinch
    Slack
    Smsapi *
    Telegram
    Twilio
    Zulip *
    Messenger
    AmazonSqs
    Amqp
    Beanstalkd *
    Doctrine
    Redis
    Mailer
    Amazon
    Google
    Mailchimp
    Mailgun
    Mailjet *
    Postmark
    Sendgrid
    to be continued...

    View Slide

  23. Mailer: Incremental improvements
    More semantic configuration
    framework:
    mailer:
    dsn: '%env(MAILER_DSN)%'
    headers:
    from: '[email protected]'
    bcc: '[email protected]'
    https://github.com/symfony/symfony/pull/36736

    View Slide

  24. {
    "htmlTemplate": "email.html.twig",
    "textTemplate": "email.txt.twig",
    "context": {
    "foo": "bar"
    },
    "text": null,
    "textCharset": null,
    "html": null,
    "htmlCharset": null,
    "attachments": [
    {
    "body": "Some Text file",
    "name": "test.txt",
    "content-type": null,
    "inline": false
    }
    ],
    "headers": {
    "to": [
    {
    "addresses": [
    {
    "address": "[email protected]",
    "name": ""
    }
    ]
    }
    ]
    },
    "body": null,
    "message": null
    }
    Mailer: Incremental improvements
    Better serialization, lightweight payload and no binaries
    https://github.com/symfony/symfony/pull/37847

    View Slide

  25. $dkimSigner = new DkimSigner($pk, 'example.com', 'sf');
    $signedEmail = $dkimSigner->sign($email);
    Mailer: Incremental improvements
    DKIM support
    Switch from Swiftmailer
    https://github.com/symfony/symfony/pull/37165

    View Slide

  26. Less code
    More automation

    View Slide

  27. Skeleton code simplification
    public/index.php
    use App\Kernel;
    use Symfony\Component\Dotenv\Dotenv;
    use Symfony\Component\ErrorHandler\Debug;
    use Symfony\Component\HttpFoundation\Request;
    require dirname(__DIR__).'/vendor/autoload.php';
    (new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
    if ($_SERVER['APP_DEBUG']) {
    umask(0000);
    Debug::enable();
    }
    if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? false) {
    Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST);
    }
    if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? false) {
    Request::setTrustedHosts([$trustedHosts]);
    }
    $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
    $request = Request::createFromGlobals();
    $response = $kernel->handle($request);
    $response->send();
    $kernel->terminate($request, $response);
    framework:
    trusted_proxies: '127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
    # or
    trusted_proxies: '%env(TRUSTED_PROXIES)%'
    # that's the defaults already
    trusted_headers: ['x-forwarded-all', '!x-forwarded-host', '!x-forwarded-prefix']
    https://github.com/symfony/symfony/pull/37357

    View Slide

  28. Skeleton code simplification
    HTTP Cache in public/index.php
    framework:
    http_cache: true
    No more PHP to enable
    PHP HTTP caching

    https://github.com/symfony/symfony/pull/37351

    View Slide

  29. Upgrade your project
    composer sync-recipes --force

    View Slide

  30. New Components

    View Slide

  31. Semaphore Component
    A semaphore allows
    N processes
    to access a resource
    A lock allows
    only 1 process
    to access a resource
    https://github.com/symfony/symfony/pull/35780

    View Slide

  32. Lock Component
    Single Write / Multiple Readers
    $aliceLock = $factory->createLock('invoice');
    $bobLock = $factory->createLock('invoice');
    $oscarLock = $factory->createLock('invoice');
    $aliceLock->acquireRead(); // true
    $bobLock->acquireRead(); // true
    $oscarLock->acquire(); // false
    https://github.com/symfony/symfony/pull/37752

    View Slide

  33. RateLimiter Component
    use Symfony\Component\RateLimiter\Storage\InMemoryStorage;
    use Symfony\Component\RateLimiter\Limiter;
    $limiter = new Limiter([
    'id' => 'login',
    'strategy' => 'token_bucket', // or 'fixed_window'
    'limit' => 10,
    'rate' => ['interval' => '15 minutes'],
    ], new InMemoryStorage());
    // blocks until 1 token is free to use for this process
    $limiter->reserve(1)->wait();
    // ... execute the code
    // only claims 1 token if it's free at this moment
    if ($limiter->consume(1)) {
    // ... execute the code
    }
    RateLimiter
    Lock
    ???
    Cache
    https://github.com/symfony/symfony/pull/37546

    View Slide

  34. Security
    reinvented

    View Slide

  35. Security Authenticators
    4 main ideas
    VerifyAuthenticatorCredentialsEvent
    LoginSuccessEvent
    LoginFailureEvent
    PUBLIC_ACCESS vs ROLE_ANONYMOUS

    View Slide

  36. Security
    Login Throttling
    RateLimiter
    Lock
    Login Throttling
    Cache
    Security
    Authenticators
    5.1
    3.3 3.1
    5.2
    5.2
    security:
    firewalls:
    default:
    # default limits to 5 login attempts per minute,
    # the number can be configured via "max_attempts"
    login_throttling: ~
    # define your own RateLimiter
    login_throttling:
    limiter: login
    https://github.com/symfony/symfony/pull/38204

    View Slide

  37. Security
    Authenticators unlock a lot of features
    Magic login link authentication
    https://github.com/symfony/symfony/pull/38177
    2fa authentication
    https://github.com/scheb/2fa
    Help needed - https://github.com/symfony/symfony/issues/30914
    Sudo mode and more

    View Slide

  38. Security
    Bootstrap a new project the fast way
    $ symfony new --version=next demo
    $ composer req maker profiler orm validator form security
    $ vi docker-compose.yml
    $ docker-compose up -d
    $ symfony server:start -d
    $ symfony open:local
    version: '3'
    services:
    database:
    image: postgres:12-alpine
    environment:
    POSTGRES_USER: main
    POSTGRES_PASSWORD: main
    POSTGRES_DB: main
    ports: [5432]
    mailer:
    image: schickling/mailcatcher
    ports: [1025, 1080]

    View Slide

  39. The name of the security user class (e.g. User) [User]:
    >
    Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]:
    >
    Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) [email]:
    >
    Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single
    sign-on server).
    Does this app need to hash/check user passwords? (yes/no) [yes]:
    >
    $ symfony console make:user

    View Slide

  40. What style of authentication do you want? [Empty authenticator]:
    [0] Empty authenticator
    [1] Login form authenticator
    > 1
    The class name of the authenticator to create (e.g. AppCustomAuthenticator):
    > AppAuthenticator
    Choose a name for the controller class (e.g. SecurityController) [SecurityController]:
    >
    Do you want to generate a '/logout' URL? (yes/no) [yes]:
    >
    $ symfony console make:auth

    View Slide

  41. Creating a registration form for App\Entity\User
    Do you want to add a @UniqueEntity validation annotation on your User class to make sure duplicate accounts aren't created? (yes/no) [yes]:
    >
    Do you want to send an email to verify the user's email address after registration? (yes/no) [yes]:
    >
    [WARNING] We're missing some important components. Don't forget to install these after you're finished.
    composer require symfonycasts/verify-email-bundle symfony/mailer
    What email address will be used to send registration confirmations? e.g. [email protected]:
    > [email protected]
    What "name" should be associated with that email address? e.g. "Acme Mail Bot":
    > Demo Bot
    Do you want to automatically authenticate the user after registration? (yes/no) [yes]:
    >
    $ symfony console make:registration-form

    View Slide

  42. --- a/src/Security/AppAuthenticator.php
    +++ b/src/Security/AppAuthenticator.php
    @@ -96,8 +96,7 @@ class AppAuthenticator extends AbstractFormLoginAuthenticator implements Passwor
    return new RedirectResponse($targetPath);
    }
    - // For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
    - throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
    + return new RedirectResponse($this->urlGenerator->generate('app_login'));
    }
    protected function getLoginUrl()
    $ symfony console make:registration-form

    View Slide

  43. Security
    Bootstrap a new project the fast way
    $ symfony make:migration
    $ symfony console doctrine:migrations:migrate -n

    View Slide

  44. https://127.0.0.1:8000/register
    $ symfony open:local:webmail

    View Slide

  45. https://127.0.0.1:8000/logout
    https://127.0.0.1:8000/login

    View Slide

  46. 2FA / MFA
    https://github.com/scheb/2fa

    View Slide

  47. &

    View Slide

  48. PHP 8 Attributes
    use Symfony\Component\Routing\Attribute\Route;
    class ActionPathController
    {
    #[Route('/{name}', name: 'hello')]
    public function action()
    {
    }
    }
    class AutowireSetter
    {
    #[Required]
    public function setFoo(Foo $foo): void
    {
    }
    }
    https://github.com/symfony/symfony/pull/37545
    https://github.com/symfony/symfony/pull/37474

    View Slide

  49. PHP 8 Attributes
    We need your help
    https://github.com/symfony/symfony/issues/38096
    https://github.com/symfony/symfony/pull/38162
    Add support for Constraints
    Think of new possibilities
    Replace SensioFrameworkExtraBundle
    public function searchAction(
    #[RequestQuery] string $q,
    #[RequestQuery] int $page = 1
    ) {

    View Slide

  50. Speed
    improvements

    View Slide

  51. Cache + Messenger =
    framework:
    cache:
    pools:
    test.cache:
    early_expiration_message_bus: messenger.default_bus
    messenger:
    routing:
    'Symfony\Component\Cache\Messenger\EarlyExpirationMessage': amqp
    https://github.com/symfony/symfony/pull/30572

    View Slide

  52. PHP 7.4 preloading
    opcache.preload=/app/src/.preload.php

    View Slide

  53. Some fun again
    French Inflector
    use Symfony\Component\String\Inflector\FrenchInflector;
    $inflector = new FrenchInflector();
    $result = $inflector->singularize('dents'); // ['dent']
    $result = $inflector->singularize('souris'); // ['souris']
    $result = $inflector->singularize('messieurs'); // ['monsieur']
    $result = $inflector->pluralize('cinquante'); // ['cinquante']
    $result = $inflector->pluralize('pou'); // ['poux']
    $result = $inflector->pluralize('cheval'); // ['chevaux']
    https://github.com/symfony/symfony/pull/36929

    View Slide

  54. https://symfony.com/sponsor
    Support Symfony
    Thank you!

    View Slide