Slide 1

Slide 1 text

Was ist neu in Symfony? PHP-Usergroup Münster, 26.02.2020

Slide 2

Slide 2 text

Christian Flothmann christian.fl[email protected] @xabbuh @xabbuh

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

4.4 Deprecations Bugfixes Bugfixes Features Features Deprecations 5.0 What are the Differences? Experimental

Slide 5

Slide 5 text

Deprecations Symfony 4.4 from 4.3 to 4.4 Code will be removed in Symfony 5.0

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Non-int return value in Command::execute() Contributed by jschaedl in #33775 protected function execute(InputInterface $input, OutputInterface $output) { // ... return 0; } Console

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Debug Component deprecated Use the new ErrorHandler Component instead!

Slide 19

Slide 19 text

WebServer Bundle $ curl -sS https://get.symfony.com/cli/installer | bash deprecated Use the Symfony CLI instead!

Slide 20

Slide 20 text

Type Declarations and Return Types https://symfony.com/blog/symfony-type-declarations-return-types https://github.com/symfony/symfony/issues/32179

Slide 21

Slide 21 text

BC-Breaks Symfony 4.4 from 4.3 to 4.4

Slide 22

Slide 22 text

- [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)

Slide 23

Slide 23 text

There is even more https://github.com/symfony/symfony/blob/master/UPGRADE-4.4.md https://github.com/symfony/symfony/blob/master/UPGRADE-5.0.md

Slide 24

Slide 24 text

Features/Improvements

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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 to select the year and week number 'widget' => 'choice', // renders two to write the year and week number 'widget' => 'text', // renders a which is properly rendered by most browsers 'widget' => 'single_text', ]); Form

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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()

Slide 31

Slide 31 text

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']

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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']

Slide 34

Slide 34 text

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; } } }

Slide 35

Slide 35 text

Service Container Linter DependencyInjection Contributed by alcalyn, GuilhemN and nicolas-grekas in #33015 $ bin/console lint:container

Slide 36

Slide 36 text

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.

Slide 37

Slide 37 text

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'

Slide 38

Slide 38 text

Mailer Integration Contributed by fabpot in #32912 WebProfilerBundle

Slide 39

Slide 39 text

HttpClient Integration Contributed by tyx and jeremyFreeAgent in #33015 WebProfilerBundle

Slide 40

Slide 40 text

Clear Ajax Requests Contributed by Matts in #31876 WebProfilerBundle

Slide 41

Slide 41 text

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(<<action('More info?', 'https://example.com/') ->importance(NotificationEmail::IMPORTANCE_HIGH) ; Mime

Slide 42

Slide 42 text

Notification Emails Contributed by fabpot in #33605 Mime

Slide 43

Slide 43 text

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 # ...

Slide 44

Slide 44 text

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); } }

Slide 45

Slide 45 text

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'

Slide 46

Slide 46 text

There is even more https://symfony.com/blog/category/living-on-the-edge/5.0-4.4 https://symfony.com/blog/symfony-4-4-curated-new-features https://symfony.com/blog/symfony-5-0-curated-new-features

Slide 47

Slide 47 text

New Components

Slide 48

Slide 48 text

ErrorHandler Component Symfony 4.4 The ErrorHandler component provides tools to manage errors and ease debugging PHP code. $ composer require symfony/error-handler

Slide 49

Slide 49 text

# public/index.php handle($request); $response->send(); $kernel->terminate($request, $response);

Slide 50

Slide 50 text

# public/index.php handle($request); $response->send(); $kernel->terminate($request, $response);

Slide 51

Slide 51 text

Class Loading Debugger use Symfony\Component\ErrorHandler\DebugClassLoader; DebugClassLoader::enable();

Slide 52

Slide 52 text

Turning PHP Errors into Exeptions use Symfony\Component\ErrorHandler\ErrorHandler; ErrorHandler::register();

Slide 53

Slide 53 text

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));

Slide 54

Slide 54 text

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.'); }

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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; });

Slide 57

Slide 57 text

{ "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

Slide 58

Slide 58 text

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'

Slide 59

Slide 59 text

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 }} }

Slide 60

Slide 60 text

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; } }

Slide 61

Slide 61 text

Custom HTML error pages based on Twig keep working as before. templates/bundles/TwigBundle/Exception/error500.html.twig

Slide 62

Slide 62 text

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.

Slide 63

Slide 63 text

Notifier Component Symfony 5.0 experimental

Slide 64

Slide 64 text

send messages via a unified API https://speakerdeck.com/fabpot/symfony-notifier

Slide 65

Slide 65 text

String Component Symfony 5.0 experimental

Slide 66

Slide 66 text

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!'

Slide 67

Slide 67 text

https://speakerdeck.com/nicolasgrekas/symfony-string-flexible-handling-of-unicode

Slide 68

Slide 68 text

Questions?

Slide 69

Slide 69 text

Thank you! https://speakerdeck.com/xabbuh

Slide 70

Slide 70 text

No content