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

Was ist neu in Symfony?

Was ist neu in Symfony?

PHP Usergroup Münster

Christian Flothmann

February 26, 2020
Tweet

More Decks by Christian Flothmann

Other Decks in Programming

Transcript

  1. Non-int return value in Command::execute() Contributed by jschaedl in #33775

    namespace App\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class CreateUserCommand extends Command { protected static $defaultName = 'app:create-user'; // ... protected function execute(InputInterface $input, OutputInterface $output) { // ... } } Console
  2. Non-int return value in Command::execute() Contributed by jschaedl in #33775

    protected function execute(InputInterface $input, OutputInterface $output) { // ... return 0; } Console
  3. Deprecated short factories/configurators YAML syntax Contributed by nicolas-grekas in #31543

    # Before services: my_service: factory: factory_service:method # After services: my_service: factory: ['@factory_service', method] DependencyInjection
  4. Deprecate int/float for string input in NumberType Contributed by xabbuh

    in #32130 use Symfony\Component\Form\Extension\Core\Type\NumberType; $form = $this->factory->create(NumberType::class, 2.99, [ 'input' => 'string', 'scale' => 2, ]); Form
  5. Deprecated HeaderBag::get() returning an array Contributed by Simperfit in #32122

    // Symfony 4.3 namespace Symfony\Component\HttpFoundation; class HeaderBag implements \IteratorAggregate, \Countable { /** * Returns a header value by name. * * @param string $key The header name * @param string|null $default The default value * @param bool $first Whether to return the first value or all header values * * @return string|string[]|null The first header value or default value if $first is true, * an array of values otherwise */ public function get($key, $default = null, $first = true) {...} } HttpFoundation
  6. Deprecated HeaderBag::get() returning an array Contributed by Simperfit in #32122

    namespace Symfony\Component\HttpFoundation; class HeaderBag implements \IteratorAggregate, \Countable { // Symfony 4.4, using argument $first is deprecated /** * @return string|string[]|null The first header value or default value if $first is * true, an array of values otherwise */ public function get($key, $default = null) {…} // Symfony 5.0 /** * @return string|null The first header value or default value */ public function get(string $key, string $default = null) {...} } HttpFoundation
  7. Deprecated HeaderBag::get() returning an array Contributed by Simperfit in #32122

    class HeaderBag implements \IteratorAggregate, \Countable { // Symfony 4.4 public function all(/*string $key = null*/) {…} // Symfony 5.0 public function all(string $key = null) {...} } HttpFoundation
  8. Deprecated passing arguments to Request::isMethodSafe() Contributed by dFayet in #31658

    // Symfony 4.3 namespace Symfony\Component\HttpFoundation; class Request /** * Checks whether or not the method is safe. * * @param bool $andCacheable Adds the additional condition that the * method should be cacheable. True by default. * * @return bool */ public function isMethodSafe(/* $andCacheable = true */) {...} } HttpFoundation
  9. Deprecated passing arguments to Request::isMethodSafe() Contributed by dFayet in #31658

    // Symfony 4.3 $request->isMethodSafe(true); // throws BadMethodCallException $request->isMethodSafe(false); // Symfony 4.4 $request->isMethodSafe(false); // deprecated // use $request->isMethodSafe(); $request->isMethodCachable(); HttpFoundation
  10. Deprecated returning non-boolean values from checkCredentials() Contributed by derrabus in

    #33308 # Symfony 4.3 # Guard\AuthenticatorInterface.php /** * Returns true if the credentials are valid. * * If any value other than true is returned, authentication will * fail. You may also throw an AuthenticationException if you wish * to cause authentication to fail. * * The *credentials* are the return value from getCredentials() * * @param mixed $credentials * @return bool * @throws AuthenticationException */ public function checkCredentials($credentials, UserInterface $user); Security
  11. Deprecated returning non-boolean values from checkCredentials() Contributed by derrabus in

    #33308 # Symfony 4.4 # Guard\AuthenticatorInterface.php /** * Returns true if the credentials are valid. * * If false is returned, authentication will fail. You may also throw * an AuthenticationException if you wish to cause authentication to fail. * * The *credentials* are the return value from getCredentials() * * @param mixed $credentials * @return bool * @throws AuthenticationException */ public function checkCredentials($credentials, UserInterface $user); Security
  12. Deprecated isGranted() on more than one attribute Contributed by wouterj

    in #33584 // Before if ($this->authorizationChecker->isGranted(['ROLE_USER', 'ROLE_ADMIN'])) { // ... } // After if ($this->authorizationChecker->isGranted( new Expression("is_granted('ROLE_USER') or is_granted(‘ROLE_ADMIN')")) ) {} // or if ($this->authorizationChecker->isGranted('ROLE_USER') || $this->authorizationChecker->isGranted('ROLE_ADMIN')) {} Security
  13. - [HttpClient] Added method cancel() to ResponseInterface. - [Mailer] Changed

    the DSN to use for disabling delivery (using the NullTransport) from smtp://null to null://null (host doesn't matter). - [Mailer] Renamed class SmtpEnvelope to Envelope and DelayedSmtpEnvelope to DelayedEnvelope. - [Mailer] Added a required string $transport argument to MessageEvent::__construct. - [Messenger] Removed SendersLocatorInterface::getSenderByAlias added in 4.3. - [Messenger] Removed $retryStrategies argument from Worker::__construct. - [Messenger] Changed arguments of ConsumeMessagesCommand::__construct. - [Messenger] Removed $senderClassOrAlias argument from RedeliveryStamp::__construct. - [Messenger] Removed UnknownSenderException. - [Messenger] Removed WorkerInterface. - [Messenger] Removed $onHandledCallback of Worker::run(array $options = [], callable $onHandledCallback = null). - [Messenger] Removed StopWhenMemoryUsageIsExceededWorker in favor of StopWorkerOnMemoryLimitListener. - [Messenger] Removed StopWhenMessageCountIsExceededWorker in favor of StopWorkerOnMessageLimitListener. - [Messenger] Removed StopWhenTimeLimitIsReachedWorker in favor of StopWorkerOnTimeLimitListener. - [Messenger] Removed StopWhenRestartSignalIsReceived in favor of StopWorkerOnRestartSignalListener. - [Mime] Removed NamedAddress, use Address instead (which supports a name now)
  14. Improved Type Constraint Contributed by jschaedl in #31351 // src/Entity/Author.php

    use Symfony\Component\Validator\Constraints as Assert; class Author { /** * @Assert\Type("Ramsey\Uuid\UuidInterface") */ protected $id; /** * @Assert\Type("string") */ protected $firstName; // ... } Validator
  15. Improved Type Constraint Contributed by jschaedl in #31351 // src/Entity/Author.php

    use Symfony\Component\Validator\Constraints as Assert; class Author { // ... /** * @Assert\Type(type={"alpha", "digit"}) */ protected $accessCode; // ... } Validator
  16. Week Form Type Contributed by dFayet in #32061 $builder->add('startDateTime', WeekType::class,

    [ // use this if you store week numbers as strings ('2011-W17') 'input' => 'string', // use this if you store week numbers as arrays (e.g. [2011, 17]) 'input' => 'array', // renders two <select> to select the year and week number 'widget' => 'choice', // renders two <input type="text"> to write the year and week number 'widget' => 'text', // renders a <input type="week"> which is properly rendered by most browsers 'widget' => 'single_text', ]); Form
  17. Added Support for Alpha-3 Codes Contributed by creiner in #33791

    use Symfony\Component\Form\Extension\Core\Type\CountryType; // ... $builder->add('country', CountryType::class, [ 'alpha3' => true, // ISO 3166-1, e.g deu ]); Form
  18. PHP Assertions for Email Messages Contributed by fabpot in #32930

    // tests/Controller/DefaultControllerTest.php use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class DefaultControllerTest extends WebTestCase { public function testSomething() { $client = static::createClient(); $client->request('GET', '/newsletter-signup'); // ... $this->assertEmailCount(2); $this->assertEmailIsQueued($this->getMailerEvent(0)); $email = $this->getMailerMessage(0); $this->assertEmailHeaderSame($email, 'To', '[email protected]'); $this->assertEmailTextBodyContains($email, 'Welcome to Symfony!'); $this->assertEmailAttachementCount($email, 1); } } FrameworkBundle
  19. PHP Assertions for Email Messages Contributed by fabpot in #32930

    FrameworkBundle $this->assertEmailCount() $this->assertQueuedEmailCount() $this->assertEmailIsQueued() $this->assertEmailIsNotQueued() $this->assertEmailAttachementCount() $this->assertEmailTextBodyContains() $this->assertEmailTextBodyNotContains() $this->assertEmailHtmlBodyContains() $this->assertEmailHtmlBodyNotContains() $this->assertEmailHasHeader() $this->assertEmailNotHasHeader() $this->assertEmailHeaderSame() $this->assertEmailHeaderNotSame() $this->assertEmailAddressContains()
  20. Simpler Event Listeners Contributed by derrabus in #33851 EventDispatcher use

    Symfony\Component\HttpKernel\Event\RequestEvent; final class MyRequestListener { public function __invoke(RequestEvent $event): void { // ... } } services: App\EventListener\MyRequestListener: tags: - - { name: kernel.event_listener, event: kernel.request } + - { name: kernel.event_listener } # config/services.yaml services: App\EventListener\: resource: ../src/EventListener/* tags: ['kernel.event_listener']
  21. Allow Binding Tagged Services Contributed by lyrixx in #33623 services:

    _instanceof: App\Foo\Rule\RuleInterface: tags: ['app.foo.rule'] _defaults: bind: iterable $rules: !tagged_iterator app.foo.rule # ... DependencyInjection
  22. Improved YAML Syntax for Method Calls Contributed by lyrixx in

    #33623 services: App\Service\MessageGenerator: # ... calls: - method: setLogger arguments: - '@logger' DependencyInjection Contributed by nicolas-grekas in #33779. services: App\Service\MessageGenerator: # ... calls: - setLogger: ['@logger']
  23. Priorities for Tagged Services Contributed by lyrixx in #33623 services:

    _instanceof: App\Handler: tags: - { name: 'app.handler', priority: 20 } App\HandlerCollection: arguments: [!tagged_iterator app.handler] DependencyInjection services: # ... App\HandlerCollection: arguments: [!tagged_iterator app.handler, default_priority_method: 'calculateServicePriority'] final class MyService { public static function getDefaultPriority(): int { return 0; } } }
  24. Service Container Linter DependencyInjection Contributed by alcalyn, GuilhemN and nicolas-grekas

    in #33015 namespace App\SomeNamespace; class SomeService { public function __construct(int $someProperty = 7) { // ... } } services: App\SomeNamespace\SomeService: $someProperty: ~ Invalid definition for service "App\SomeNamespace\SomeService": argument 1 of "App\SomeNamespace\SomeService::__construct" accepts "int", "NULL" passed.
  25. Service Container Linter DependencyInjection Contributed by alcalyn, GuilhemN and nicolas-grekas

    in #33015 namespace App\SomeNamespace; class SomeService { public function setSomeItems( SomeClass $item, SomeClass …$items ) { // ... } } Invalid definition for service "App\SomeNamespace\SomeService": argument 2 of "App\SomeNamespace\SomeService::setSomeItems" accepts "App\SomeNamespace\SomeClass", "App\AnotherNamespace\SomeDifferentClass" passed. services: foo: class: App\SomeNamespace\SomeClass bar: class: App\AnotherNamespace\SomeDifferentClass App\SomeNamespace\SomeService: calls: - method: setSomeItems arguments: - '@foo' - '@bar'
  26. Notification Emails Contributed by fabpot in #33605 use Symfony\Bridge\Twig\Mime\NotificationEmail; $email

    = (new NotificationEmail()) ->from('[email protected]') ->to('[email protected]') ->subject('My first notification email via Symfony') ->markdown(<<<EOF There is a **problem** on your website, you should investigate it right now. Or just wait, the problem might solves itself automatically, we never know. EOF ) ->action('More info?', 'https://example.com/') ->importance(NotificationEmail::IMPORTANCE_HIGH) ; Mime
  27. Lazy Firewalls Contributed by nicolas-grekas in #33676 # config/packages/security.yaml security:

    # ... firewalls: main: pattern: ^/ anonymous: ~ # ... Security # config/packages/security.yaml security: # ... firewalls: main: pattern: ^/ anonymous: lazy # ...
  28. Password Migrations Contributed by nicolas-grekas in #31594, #31597 and #31843

    # config/packages/security.yaml security: # ... encoders: App\Entity\User: algorithm: auto cost: 14 Security use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; class UserRepository extends EntityRepository implements PasswordUpgraderInterface { public function upgradePassword(UserInterface $user, string $newEncodedPassword): void { // this code is only an example; the exact code will depend on $user->setPassword($newEncodedPassword); $this->getEntityManager()->flush($user); } }
  29. Password Hashing Contributed by chalas_r in #34020 and #34139 security:

    # ... encoders: App\Entity\User: algorithm: 'argon2i' algorithm: 'argon2id' algorithm: 'auto' algorithm: 'bcrypt' algorithm: 'sodium' Security # config/packages/security.yaml security: # ... encoders: App\Entity\User: algorithm: 'argon2i' migrate_from: 'bcrypt'
  30. ErrorHandler Component Symfony 4.4 The ErrorHandler component provides tools to

    manage errors and ease debugging PHP code. $ composer require symfony/error-handler
  31. # public/index.php <?php use App\Kernel; // Before use Symfony\Component\Debug\Debug; require

    dirname(__DIR__).'/config/bootstrap.php'; if ($_SERVER['APP_DEBUG']) { umask(0000); Debug::enable(); } // ... $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send(); $kernel->terminate($request, $response);
  32. # public/index.php <?php use App\Kernel; // After use Symfony\Component\ErrorHandler\Debug; require

    dirname(__DIR__).'/config/bootstrap.php'; if ($_SERVER['APP_DEBUG']) { umask(0000); Debug::enable(); } // ... $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send(); $kernel->terminate($request, $response);
  33. Catching PHP Function Errors and turning them into Exceptions data

    = json_decode(file_get_contents($filename), true); $data['read_at'] = date($datetimeFormat); file_put_contents($filename, json_encode($data));
  34. Catching PHP Function Errors and turning them into Exceptions $content

    = @file_get_contents($filename); if (false === $content) { throw new \RuntimeException('Could not load file.'); } $data = @json_decode($content, true); if (null === $data) { throw new \RuntimeException('File does not contain valid JSON.'); } $datetime = @date($datetimeFormat); if (false === $datetime) { throw new \RuntimeException('Invalid datetime format.'); }
  35. Catching PHP Function Errors and turning them into Exceptions $content

    = ErrorHandler::call('file_get_contents', $filename);
  36. Catching PHP Function Errors and turning them into Exceptions $data

    = ErrorHandler::call(static function () use ($filename, $datetimeFormat) { // if any code executed inside this anonymous function fails, // a PHP exception will be thrown, even if the code // uses the '@' PHP silence operator $data = json_decode(file_get_contents($filename), true); $data['read_at'] = date($datetimeFormat); file_put_contents($filename, json_encode($data)); return $data; });
  37. { "title": "Not Found", "status": 404, "detail": "Sorry, the page

    you are looking for could not be found" } Error pages for non-HTML formats templates/bundles/TwigBundle/Exception/error403.json.twig Request Format JSON XML ATOM TXT RFC 7807 deprecated
  38. Deprecated error templates for non-html formats Contributed by yceruto in

    #31398 # If you were not using this option previously, set it to `null` twig: exception_controller: null # If you were using this option previously, set it to `null` # and use `framework.error_controller` instead # Before twig: exception_controller: 'App\Controller\MyExceptionController' # After twig: exception_controller: null framework: error_controller: 'App\Controller\MyExceptionController'
  39. How to customize error pages for non-HTML formats? // templates/bundles/TwigBundle/Exception/error.json.twig

    { "type": "https://example.com/error", "title": "{{ status_text }}", "status": {{ status_code }} }
  40. How to customize error pages for non-HTML formats? class ProblemJsonNormalizer

    implements NormalizerInterface { public function normalize($exception, $format = null, array $context = []) { return [ 'type' => 'https://example.com/error', 'title' => $exception->getStatusText(), 'status' => $exception->getStatusCode(), ]; } public function supportsNormalization($data, $format = null) { return 'json' === $format && $data instanceof FlattenException; } }
  41. Custom HTML error pages based on Twig keep working as

    before. templates/bundles/TwigBundle/Exception/error500.html.twig
  42. Error preview pages - # config/routes/dev/twig.yaml + # config/routes/dev/framework.yaml _errors:

    - resource: '@TwigBundle/Resources/config/routing/errors.xml' + resource: '@FrameworkBundle/Resources/config/routing/errors.xml' prefix: /_error The error page preview feature keeps working as before, but some files have changed their location.
  43. use function Symfony\Component\String\u; $text = u('This is a déjà-vu situation.')

    ->trimEnd('.') ->replace('déjà-vu', 'jamais-vu') ->append('!'); // $text = 'This is a jamais-vu situation!'