Slide 1

Slide 1 text

Symfony 7: A tribute to Attributes @nicolasgrekas

Slide 2

Slide 2 text

👌 👍 Auto-discovery Auto-configuration Auto-wiring #[Attributes]

Slide 3

Slide 3 text

symfony.com/releases @nicolasgrekas

Slide 4

Slide 4 text

Backward Compatibility Promise â€ĸ Upgrade every month for bug fixes â€ĸ Upgrade every 6 months for features @nicolasgrekas "extra": { "symfony": { "require": "6.4.*" } }

Slide 5

Slide 5 text

Continuous Upgrade Path â€ĸ Fix deprecations every 6 months â€ĸ Upgrade every 2 years for new major @nicolasgrekas "require": { "php": ">=8.2" }

Slide 6

Slide 6 text

🎉 Symfony 7 â€ĸ Remove deprecated code paths â€ĸ Add native return types â€ĸ Add native types to public|protectedproperties @nicolasgrekas

Slide 7

Slide 7 text

RIP Doctrine Annotations ⚠ī¸ PHP 8 introduced attributes, which are a native replacement for annotations @nicolasgrekas

Slide 8

Slide 8 text

RIP SensioFrameworkExtraBundle @Route @Entity @IsGranted @Security @ParamConverter @Cache @Template @nicolasgrekas

Slide 9

Slide 9 text

😍 #[Attributes] @nicolasgrekas #[Route(path: '/', name: 'home')] // return list ReflectionMethod::getAttributes(); // return new Route(path: '/', name: 'home'); ReflectionAttribute::newInstance(); // return ['path' => '/', 'name' => 'home']; ReflectionAttribute::getArguments();

Slide 10

Slide 10 text

Attributes are ignorable; they don't create coupling Attributes are useful hints telling how some code could be used @nicolasgrekas

Slide 11

Slide 11 text

@nicolasgrekas 422 Unprocessable Entity? class DiscountExpired extends DiscountException { // ... }

Slide 12

Slide 12 text

@nicolasgrekas Decoupled! #[WithHttpStatus(422)] class DiscountExpired extends DiscountException { // ... }

Slide 13

Slide 13 text

@nicolasgrekas services: _defaults: autowire: true # Look at parameter types to auto-inject deps autoconfigure: true # Look at parent types to auto-register classes # Load every class in a namespace/directory as a service App\: resource: '../src/' # Add more service definitions when explicit configuration is needed config/services.yaml

Slide 14

Slide 14 text

@nicolasgrekas FrameworkBundle's FrameworkExtension $container->registerForAutoconfiguration(Command::class) $container->registerForAutoconfiguration(AbstractController::class) $container->registerForAutoconfiguration(FormTypeInterface::class) $container->registerForAutoconfiguration(EventSubscriberInterface::class) $container->registerForAutoconfiguration(ResetInterface::class) $container->registerForAutoconfiguration(MessageHandlerInterface::class) // ...

Slide 15

Slide 15 text

@nicolasgrekas FrameworkBundle's FrameworkExtension $container->registerAttributeForAutoconfiguration(AsEventListener::class, ...) $container->registerAttributeForAutoconfiguration(AsController::class, ...) $container->registerAttributeForAutoconfiguration(AsRemoteEventConsumer::class, ...) $container->registerAttributeForAutoconfiguration(AsMessageHandler::class, ...) $container->registerAttributeForAutoconfiguration(AsSchedule::class, ...) // ...

Slide 16

Slide 16 text

@nicolasgrekas Just Enjoy on your side #[AsEventListener] class MyRequestListener { public function __invoke(RequestEvent $event): void { // ... } }

Slide 17

Slide 17 text

@nicolasgrekas #[AutoconfigureTag] interface ChannelInterface { } Define your rules

Slide 18

Slide 18 text

@nicolasgrekas Enjoy your rules class ChannelBroadcaster { public function __construct( #[AutowireIterator(ChannelInterface::class)] iterable $channels ) { // ... }

Slide 19

Slide 19 text

@nicolasgrekas #[AsAlias(ChannelInterface::class)] class DefaultChannel implements ChannelInterface {

Slide 20

Slide 20 text

🧙‍♀ī¸ Dependency Injection #[AsDecorator] #[AsTaggedItem] #[AutowireCallable] #[AutowireDecorated] #[AutowireLocator] #[AutowireServiceClosure] #[Exclude] #[Required] #[SubscribedService] #[Target] #[When] @nicolasgrekas

Slide 21

Slide 21 text

@nicolasgrekas #[Autoconfigure(lazy: true)] class PlotFactory { public function __construct( #[Autowire(param: 'kernel.debug')] private bool $debug, private HeavyDependency $heavyDependency, ) { } }

Slide 22

Slide 22 text

public function __construct( #[AutowireLocator([ 'router' => RouterInterface::class, 'slugger' => '?'.SluggerInterface::class, ])] ContainerInterface $container, )

Slide 23

Slide 23 text

@nicolasgrekas public function __construct( #[AutowireCallable(UriTemplate::class, 'expand')] UriExpanderInterface $expander, ) new class($uriTemplate->expand(...)) implements UriExpanderInterface { public function __construct( private \Closure $closure, ) { } public function expand(string $url, array $vars): string { return $this->closure->__invoke($url, $vars); } } $uriTemplate->expand(...)

Slide 24

Slide 24 text

🕹ī¸ Controllers #[CurrentUser] #[MapDateTime] #[MapEntity] #[MapQueryParameter] #[MapQueryString] #[MapRequestPayload] #[ValueResolver] @nicolasgrekas

Slide 25

Slide 25 text

@nicolasgrekas #[Route('/blog')] public function blog( #[MapQueryParameter] int $page = 1, )

Slide 26

Slide 26 text

@nicolasgrekas #[Route('/product-review', methods: ['POST'])] public function post( #[MapRequestPayload] ProductReviewDto $productReview, )

Slide 27

Slide 27 text

@nicolasgrekas class ProductReviewDto { public function __construct( #[Assert\NotBlank] #[Assert\Length(min: 10, max: 500)] public readonly string $comment, #[Assert\GreaterThanOrEqual(1)] #[Assert\LessThanOrEqual(5)] public readonly int $rating, ) { // ...

Slide 28

Slide 28 text

🛡ī¸ Validation #[NoSuspiciousCharacters] #[NotCompromisedPassword] #[PasswordStrength] @nicolasgrekas

Slide 29

Slide 29 text

What's next? 💡 @nicolasgrekas

Slide 30

Slide 30 text

⛓ Contracts? #[Deprecated]? #[Internal]? #[Final]? @nicolasgrekas

Slide 31

Slide 31 text

🔧 Config? #[AutowireHttpClient(base_uri: etc.)]? @nicolasgrekas

Slide 32

Slide 32 text

Credits â™Ĩ @nicolasgrekas

Slide 33

Slide 33 text

Hundreds of contributors @nicolasgrekas

Slide 34

Slide 34 text

Backers for 6.3 @nicolasgrekas symfony.com/sponsor

Slide 35

Slide 35 text

symfony.com/slack Mutual aid Kindness CARE team @nicolasgrekas

Slide 36

Slide 36 text

Thank you and see you soon! Online: