Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Jan Schädlich jan.schaedlich@sensiolabs.de @jschaedl @janschaedlich

Slide 3

Slide 3 text

Looking for a Job? https://sensiolabs.de/jobs/96454

Slide 4

Slide 4 text

Agenda Deprecations Improvements New Components

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 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 9

Slide 9 text

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

Slide 10

Slide 10 text

Deprecated tag !tagged in favor of !tagged_iterator Contributed by jschaedl in #31321 # Before services: App\Handler: tags: ['app.handler'] App\HandlerCollection: arguments: [!tagged app.handler] # After services: App\Handler: tags: ['app.handler'] App\HandlerCollection: arguments: [!tagged_iterator app.handler] DependencyInjection

Slide 11

Slide 11 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 12

Slide 12 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 13

Slide 13 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 14

Slide 14 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 15

Slide 15 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 16

Slide 16 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 17

Slide 17 text

Deprecated passing arguments to Request::isMethodSafe() Contributed by dFayet in #31658 // Symfony 4.3 $request->isMethodSafe(true); // throws BadMethodCallException $request->isMethodSafe(); // throws BadMethodCallException $request->isMethodSafe(false); // Symfony 4.4 $request->isMethodSafe(false); // deprecated // use $request->isMethodSafe(); $request->isMethodCachable(); HttpFoundation

Slide 18

Slide 18 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 19

Slide 19 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 20

Slide 20 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 21

Slide 21 text

Debug Component deprecated Use the new ErrorHandler Component instead!

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

└── MyBundle/ ├── config/ ├── public/ ├── src/ │ └── MyBundle.php ├── templates/ └── translations class MyBundle extends Bundle { public function getPath(): string { return \dirname(__DIR__); } } Added new Bundle directory convention consistent with standard skeleton HttpKernel

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

BC-Breaks Symfony 4.4 from 4.3 to 4.4

Slide 26

Slide 26 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 27

Slide 27 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 28

Slide 28 text

Pause?

Slide 29

Slide 29 text

Improvements

Slide 30

Slide 30 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 31

Slide 31 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 32

Slide 32 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 33

Slide 33 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', 'fabien@symfony.com'); $this->assertEmailTextBodyContains($email, 'Welcome to Symfony!'); $this->assertEmailAttachementCount($email, 1); } } FrameworkBundle

Slide 34

Slide 34 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 35

Slide 35 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 36

Slide 36 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 37

Slide 37 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 38

Slide 38 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 39

Slide 39 text

Mailer Integration Contributed by fabpot in #32912 WebProfilerBundle

Slide 40

Slide 40 text

HttpClient Integration Contributed by tyx and jeremyFreeAgent in #33015 WebProfilerBundle

Slide 41

Slide 41 text

Clear Ajax Requests Contributed by Matts in #31876 WebProfilerBundle

Slide 42

Slide 42 text

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

Slide 43

Slide 43 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: ~ Invalid definition for service "App\SomeNamespace\SomeService": argument 1 of "App\SomeNamespace\SomeService::__construct" accepts "int", "NULL" passed.

Slide 44

Slide 44 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 45

Slide 45 text

Notification Emails Contributed by fabpot in #33605 use Symfony\Bridge\Twig\Mime\NotificationEmail; $email = (new NotificationEmail()) ->from('fabien@example.com') ->to('fabien@example.org') ->subject('My first notification email via Symfony') ->markdown(<<action('More info?', 'https://example.com/') ->importance(NotificationEmail::IMPORTANCE_HIGH) ; Mime

Slide 46

Slide 46 text

Notification Emails Contributed by fabpot in #33605 Mime

Slide 47

Slide 47 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 48

Slide 48 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 49

Slide 49 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 50

Slide 50 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 51

Slide 51 text

Preloading Symfony Applications in PHP 7.4

Slide 52

Slide 52 text

OPCache Preloading in Practice ; php.ini opcache.preload=/path/to/the/preload.php

Slide 53

Slide 53 text

OPCache Preloading in Symfony ; php.ini opcache.preload=/path/to/project/var/cache/prod/App_KernelProdContainer.preload.php

Slide 54

Slide 54 text

OPCache Preloading in Symfony namespace Symfony\Component\HttpKernel; abstract class Kernel implements KernelInterface, RebootableInterface, TerminableInterface { /** * Gets the classes to list in the preloading script. * * When a class is listed, all its parent classes or interfaces are automatically listed too. * Service classes are also automatically preloaded and don't need to be listed explicitly. */ public function getClassesToPreload(): array { //... } }

Slide 55

Slide 55 text

OPCache Preloading in Symfony namespace Symfony\Component\HttpKernel\DependencyInjection; abstract class Extension extends BaseExtension { /** * Adds classes to list in the preloading script. * * When a class is listed, all its parent classes or interfaces are automatically listed too. * Service classes are also automatically preloaded and don't need to be listed explicitly. */ public function addClassesToPreload(array $preloadedClasses): void { //... } }

Slide 56

Slide 56 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 57

Slide 57 text

Pause?

Slide 58

Slide 58 text

New Components

Slide 59

Slide 59 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 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 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 65

Slide 65 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 66

Slide 66 text

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

Slide 67

Slide 67 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 68

Slide 68 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 69

Slide 69 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 70

Slide 70 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 71

Slide 71 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 72

Slide 72 text

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

Slide 73

Slide 73 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 74

Slide 74 text

Notifier Component Symfony 5.0 experimental https://www.meetup.com/de-DE/sfughh/events/xqdjjrybcdbgb/

Slide 75

Slide 75 text

String Component Symfony 5.0 experimental https://www.meetup.com/de-DE/sfughh/events/xqdjjrybcdbgb/

Slide 76

Slide 76 text

Questions?

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

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