Tips
by Joujou
03/06/2024

by Joujou
- 03/06/2024
Attributs
by Joujou
- 03/06/2024
Attributs
<?php
//...
use Symfony\Component\Routing\Annotation\Route;
class BlogController extends AbstractController
{
/**
* @Route("/blog", name="blog_list")
*/
public function list(): Response
{
// ...
}
}Avant
by Joujou
- 03/06/2024
Attributs
<?php
// ...
/**
* @Route("/blog", name="blog_list")
*/
// ...Avant
Après
<?php
// ...
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
#[Route('/blog', name: 'blog_list')]
public function list(): Response
by Joujou
- 03/06/2024
Attributs
Symfony 6.4+
- Basiques (Route, Doctrine, etc.)
- AsController
- CurrentUser (seulement dans les controllers)
- Autowire
- AutoconfigureTag
- TaggedIterator
- When
- AsDecorates
- AsCronTask
- AsPeriodicTask
- AsEventListener
- Cache
- MapQueryParameter
- MapRequestPayload
by Joujou
- 03/06/2024
Attributs
AsController
<?php
// ...
use Symfony\Component\HttpKernel\Attribute\AsController;
#[AsController]
class HelloController
{
#[Route('/hello', name: 'hello', methods: ['GET'])]
public function index(): Response
{
// ...
}
}Pas de configuration YAML
by Joujou
- 03/06/2024
Attributs
CurrentUser
<?php
// ...
use Symfony\Component\Security\Http\Attribute\CurrentUser;
#[AsController]
class HelloController
{
#[Route('/hello', name: 'hello', methods: ['GET'])]
public function index(#[CurrentUser] ?User $user): Response
{
// ...
}
}⚠️ Uniquement dans les controllers ⚠️
by Joujou
- 03/06/2024
Attributs
Autowire
<?php
//...
use Symfony\Component\DependencyInjection\Attribute\Autowire;
class MessageGenerator
{
public function __construct(
#[Autowire(service: 'monolog.logger.request')]
private LoggerInterface $logger,
) {
// ...
}
}by Joujou
- 03/06/2024
Attributs
AutoconfigureTag
<?php
//...
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag('app.custom_tag')]
interface CustomInterface
{
// ...
}Plus besoin de la configuration YAML
services:
_instanceof:
App\Security\CustomInterface:
tags: ['app.custom_tag']
by Joujou
- 03/06/2024
Attributs
TaggedIterator
<?php
//...
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
class HandlerCollection
{
public function __construct(
#[TaggedIterator('app.custom_tag')]
iterable $handlers
) {
// ...
}
}Plus besoin de la configuration YAML
by Joujou
- 03/06/2024
Attributs
When
<?php
//...
use Symfony\Component\DependencyInjection\Attribute\When;
#[When(env: 'prod')]
class UberallReviewClient
{
// ...
}
#[When(env: 'dev')]
#[When(env: 'test')]
class DecoratorGoogleMyBusinessReviewClient
{
// ...
}by Joujou
- 03/06/2024
Attributs
AsDecorator
<?php
//...
#[AsDecorator(decorates: UberallReviewClient::class)]
class DecoratorGoogleMyBusinessReviewClient implement ReviewClientInterface
{
public function __construct(
#[AutowireDecorated] private object $inner
) {
// ...
}
public function getList(Store $store): array
{
return $this->inner->getList($store);
}
}Plus besoin de la configuration YAML
by Joujou
- 03/06/2024
Attributs
AsCronTask
<?php
//...
use Symfony\Component\Scheduler\Attribute\AsCronTask;
#[AsCronTask('0 0 * * *')]
class MyTask
{
public function __invoke()
{
// ...
}
}
#[AsCronTask('0 0 * * *', method: 'foo', arguments: ['bar' => 'truite'])]
class MyTask
{
public function foo(string $bar): void
{
// ...
}
}by Joujou
- 03/06/2024
Attributs
AsPeriodicTask
<?php
//...
use Symfony\Component\Scheduler\Attribute\AsPeriodicTask;
#[AsPeriodicTask(frequency: '1 day', from: '2024-01-01', until: '2024-06-12')]
class MySeasonalTask
{
public function __invoke(): void
{
// ...
}
}by Joujou
- 03/06/2024
Attributs
AsEventListener
<?php
//...
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener(event: CustomEvent::class, method: 'onCustomEvent')]
#[AsEventListener(event: 'bar', method: 'onBarEvent')]
final class MyMultiListener
{
public function onCustomEvent(CustomEvent $event): void
{
// ...
}
public function onBarEvent(): void
{
// ...
}
#[AsEventListener(event: 'foo', priority: 42)]
public function onFoo(): void
{
// ...
}
}by Joujou
- 03/06/2024
Attributs
Cache
<?php
//...
use Symfony\Component\HttpKernel\Attribute\Cache;
#[Cache(public: true, maxage: 3600, mustRevalidate: true)]
public function index(): Response
{
return $this->render('blog/index.html.twig', []);
}by Joujou
- 03/06/2024
Attributs
MapQueryParameter
<?php
//...
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
public function dashboard(
#[MapQueryParameter(
filter: \FILTER_VALIDATE_REGEXP,
options: ['regexp' => '/^\w+$/']
)] string $firstName,
#[MapQueryParameter] string $lastName,
#[MapQueryParameter(filter: \FILTER_VALIDATE_INT)] int $age,
): Response
{
// ...
}by Joujou
- 03/06/2024
Attributs
MapRequestPayload
<?php
//...
use App\Model\UserDto;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
public function dashboard(
#[MapRequestPayload] UserDto $userDto
): Response
{
// ...
}by Joujou
- 03/06/2024
Miscellaneous
by Joujou
- 03/06/2024
Promotion du Constructeur
<?php
class UberallReviewClient implements ReviewClientInterface
{
private string $baseUri;
public function __construct(
private EmojiService $emojiService,
private LoggerInterface $reviewLogger,
private int $apiKey
) {
$this->baseUri = 'dummy';
}
}by Joujou
- 03/06/2024
Readonly
<?php
class Review
{
public function __construct(
readonly public string $id,
readonly public string $author,
public ?DateTimeInterface $publishedAt = null,
) {
}
public function publish(): self
{
$this->$publishedAt = new \DateTime();
}
}Propriétés
by Joujou
- 03/06/2024
Readonly
<?php
readonly class Review
{
public function __construct(
public string $id,
public string $author,
public ?DateTimeInterface $publishedAt,
) {
}
}Classes
by Joujou
- 03/06/2024
Union / Intersection types
<?php
class Example {
private int|float $foo;
public function squareAndAdd(float|int $bar): int|float
{
return $bar ** 2 + $foo;
}
function countAndIterate(\Iterator&\Countable $value): void
{
foreach($value as $val) {}
\count($value);
}
}
by Joujou
- 03/06/2024
Nullsafe Operator
<?php
// Avant
if (null === $repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (!$user instanceof User)) {
$result = null;
} else {
$result = $user->name;
}
}
// Après
$result = $repository?->getUser(5)?->name;
by Joujou
- 03/06/2024
Match
<?php
private function getMedia(Store $store, string $type): ?Media
{
return match ($type) {
self::IMAGE => $store->getImageStore(),
self::IMAGE_EXTERIOR => $store->getImageStoreExterior(),
self::IMAGE_INTERIOR => $store->getImageStoreInterior(),
default => throw new \InvalidArgumentException(
'This type is not supported : ' . $type
),
};
}
by Joujou
- 03/06/2024
Arguments nommés
<?php
$reviewDTOs[] = new Review(
id: $review->getId(),
author: $review->getAuthor(),
content: $review->getContent(),
rate: $review->getRate() ?: 0
);
by Joujou
- 03/06/2024
Arguments nommés
<?php
$filter = new Filter(
null,
null,
$storeIds ?? null,
$directories ?? null,
$suppliers ?? $brandSuppliers,
null,
$resourceDto->getLimit(),
null,
null,
null,
null,
$resourceDto->getSortBy(),
$resourceDto->getOrderBy(),
null,
$resourceDto->getPageToken()
);
Avant
by Joujou
- 03/06/2024
Arguments nommés
<?php
$filter = new Filter(
storeIds: $storeIds ?? null,
directories: $directories ?? null,
supplierSlugs: $suppliers ?? $brandSuppliers,
limit: $resourceDto->getLimit(),
orderBy: $resourceDto->getSortBy(),
orderDir: $resourceDto->getOrderBy(),
pageToken: $resourceDto->getPageToken()
);
Après
by Joujou
- 03/06/2024
Enumération
<?php
enum Suit
{
case Hearts;
case Diamonds;
case Clubs;
case Spades;
}
function pick_a_card(Suit $suit)
{
/* ... */
}
$val = Suit::Diamonds;
// OK
pick_a_card($val);
// OK
pick_a_card(Suit::Clubs);
// TypeError: pick_a_card(): Argument #1 ($suit) must be of type Suit, string given
pick_a_card('Spades');Basique
by Joujou
- 03/06/2024
Enumération
<?php
enum Suit: string
{
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
print Suit::Clubs->value; // Affiche "C"
$hearts = Suit::tryFrom('H'); // La valeur est Suit::HeartsBacked
by Joujou
- 03/06/2024
Enumération
<?php
trait EnumToArray
{
public static function names(): array
{
return \array_column(self::cases(), 'name');
}
public static function values(): array
{
return \array_column(self::cases(), 'value');
}
public static function asArray(): array
{
if (empty(self::values())) {
return self::names();
}
if (empty(self::names())) {
return self::values();
}
return \array_column(self::cases(), 'value', 'name');
}
}
Backed
by Joujou
- 03/06/2024
Enumération
<?php
enum Suit: string
{
use EnumToArray;
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
Suit::names() => ['Hearts', 'Diamonds', 'Clubs', 'Spades']
Suit::values() => ['H', 'D', 'C', 'S']
$test = 'H';
if (\in_array($test, Suit::values(), true)) {
// ...
}Backed
by Joujou
- 03/06/2024
Constante
<?php
class Dummy {
final const string NAME = 'dummy';
}
$name = 'NAME';
echo Dummy::{$name}; // Affiche 'dummy'Merci
Questions ?
PHP 8
By Nicolas Jourdan
PHP 8
- 53