![Alex Rock avatar](//fr.gravatar.com/userimage/41973088/0fbe36bcaaf555341ef5cd2fd1017128.jpg?size=250) **Alex Rock** PHP / Symfony dev & trainer `@pierstoval`

Quoi de neuf chez Symfony ?

Programme : * Programme * Interactivité * Symfony 3.x * Symfony 4.x * Création d'un projet Flex

Conférence INTERACTIVE

Pour participer, accédez à cette url: [piers.ovh/sf4](https://piers.ovh/sf4)

Petit rappel

Symfony c'est : * Ses créateurs : [SensioLabs](https://sensiolabs.com) * Ses contributeurs : Symfony [core team](https://symfony.com/doc/current/contributing/code/core_team.html) et la [communauté](https://symfony.com/contributors) * Un framework full-stack (50 packages) * Des packages stand-alones : 36 composants, 6 bundles et 5 bridges * Des outils externes au framework comme Flex et Webpack Encore * Vous ! ☺

# Symfony 4.0

Merci !




@pierstoval


Je plaisante 😉. On continue →

Résumé

| Version | _Features_ | Sortie | Fin de maintenance | Fin de vie | | :---: | :---: | :---: | :---: | :---: | | 3.0 | 0 | 11/2015 | 07/2016 | 01/2017 | | 3.1 | 131 | 05/2016 | 01/2017 | 07/2017 | | 3.2 | 66 | 11/2016 | 07/2017 | 01/2018 | | 3.3 | 267 | 05/2017 | 01/2018 | 07/2018 | | 3.4 | 215 | 11/2017 | 11/2020 | 11/2021 | | 4.0 | 0 | 11/2017 | 07/2018 | 01/2019 |

# Symfony 3.1

3.1: Validation d'image stricte

use Symfony\Component\Validator\Constraints as Assert;

class Product
{
    /**
     * @Assert\Image(
     *     detectCorrupted = true,
     *     corruptedMessage = "Product photo is corrupted. Upload it again."
     * )
     */
    protected $photo;
}
Source: https://symfony.com/blog/new-in-symfony-3-1-strict-image-validation

3.1: Taille des colonnes dans les tableaux en console

use Symfony\Component\Console\Helper\Table;

$table = new Table($output);
$table
    ->setHeaders(...)
    ->setRows(array(...))
    ->setColumnWidth(0, 15)
    ->setColumnWidth(3, 10)
;
$table->render();
Source: https://symfony.com/blog/new-in-symfony-3-1-explicit-column-widths-in-console-tables

3.1: Flux d'entrée pour les Process

use Symfony\Component\Process\Process;
use Symfony\Component\Process\InputStream;

$input = new InputStream();
$input->write('the');

$process = new Process('my_script');
$process->setInput($input);
$process->start();

$input->write('awesome');
$input->write('conference');
$input->close();
use Symfony\Component\Process\Process;

$process = new Process('cat');
$process->setInput('file.txt');
$process->run();

Source: https://symfony.com/blog/new-in-symfony-3-1-input-and-output-stream-for-processes

3.1: Flux de sortie pour les Process

use Symfony\Component\Process\Process;

$process = new Process('ls -lsa');
$process->start();

foreach ($process as $type => $data) {
    if ($type === Process::OUT) {
        echo $data."\n";
    } elseif ($type === Process::ERR) {
        echo "[ERR] ".$data."\n";
    }
}
Source: https://symfony.com/blog/new-in-symfony-3-1-input-and-output-stream-for-processes

3.1: Améliorations de la _debug toolbar_ et du _profiler_

* Affichage des erreurs "silencieuses" dans un onglet à part. * Recherche d'un profil par code HTTP. * Un contexte plus verbeux est ajouté dans certaines instructions dans les logs. * Affiche le code de réponse HTTP pour les requêtes AJAX.

Source: https://symfony.com/blog/new-in-symfony-3-1-web-debug-toolbar-and-profiler-enhancements

3.1: Data URI Normalizer

use Symfony\Component\Serializer\Normalizer\DataUriNormalizer;

$normalizer = new DataUriNormalizer();

$avatar = $normalizer->normalize(new \SplFileObject('avatar.gif'));
// $avatar = 'data:image/gif;base64,R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=';

$avatar = $normalizer->denormalize('data:image/gif;base64,R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=', 'SplFileObject');
// $avatar is a SplFileObject with the GIF image contents
Source: https://symfony.com/blog/new-in-symfony-3-1-data-uri-normalizer

3.1: _Mocks_ pour les tests liés au réseau ou à des DNS

/** @group dns-sensitive */
public function testEmails()
{
    DnsMock::withMockedHosts([ 'example.com' => [
        ['type' => 'MX'],
        // ['type' => 'A', 'ip' => '1.2.3.4'],
        // ['type' => 'AAAA', 'ipv6' => '::12'],
    ]]);

    $constraint = new \Symfony\Component\Validator\Constraints\Email(['checkMX' => true]);

    $result = $this->createValidator()->validate('foo@example.com', $constraint);
}
Source: https://symfony.com/blog/new-in-symfony-3-1-network-mocking-and-dns-sensitive-tests

3.1: Panneau _Security_ dans le profiler

* Rajoute les infos de l'utilisateur connecté (nom, token...). * Log des actions des _voters_.

Source: https://symfony.com/blog/new-in-symfony-3-1-improved-the-security-profiler-panel

3.1: DateTime Normalizer

use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Serializer;

$serializer = new Serializer(array(new DateTimeNormalizer()));

$dateAsString = $serializer->normalize(new \DateTime('2016/01/01'));
// $dateAsString = '2016-01-01T00:00:00+00:00';

$dateAsObject = $serializer->denormalize('2016-01-01T00:00:00+00:00', \DateTime::class));
// $dateAsObject = class DateTime#1 (3) {
//   public $date => string(26) "2016-01-01 00:00:00.000000"
//   public $timezone_type => int(1)
//   public $timezone => string(6) "+00:00"
// }
Source: https://symfony.com/blog/new-in-symfony-3-1-datetime-normalizer

3.1: Améliorations du _Deprecations Helper_

Création de la variable d'environnement `SYMFONY_DEPRECATIONS_HELPER` : * `disabled`, désactive la gestion des dépréciations. * Une regex de type `/Passing callable strings .*|Passing a boolean flag .*/` * Un nombre maximum de dépréciations avant de considérer la suite comme échouée. * `weak`, ne fait pas échouer la suite de tests s'il y a des dépréciations. * `weak_vendors`, comme `weak` mais uniquement pour les dépréciations venant des vendors.

Source: https://symfony.com/blog/new-in-symfony-3-1-deprecation-helper-improvements

3.1: Améliorations du `FrameworkBundle`

* Les `FormType` du framework définis en tant que services sont dépréciés. * Rajouté une méthode `json()` dans le contrôleur de base: ``` public function indexAction() { // Avant return new JsonResponse($data); // Après return $this->json($data); } ```

Source: https://symfony.com/blog/new-in-symfony-3-1-frameworkbundle-improvements

3.1: Améliorations de la _debug toolbar_ et du _profiler_

* Les redirections et les `forward` sont visibles dans la toolbar et dans le profiler. * Les sous-requêtes sont désormais affichées dans un onglet dédié du profiler.

Source: https://symfony.com/blog/new-in-symfony-3-1-forwards-and-redirects-in-the-toolbar-and-profiler

3.1: Dépréciations YAML

parameters:
    # Avant
    my_object: '!!php/object:O:27:"AppBundle\Service\MyService":1:{s:1:"b";s:3:"foo";}'
    # Après
    my_object: '!php/object:O:27:"AppBundle\Service\MyService":1:{s:1:"b";s:3:"foo";}'
use Symfony\Component\Yaml\Dumper;

// Avant
$yaml = new Dumper();
$yaml->setIndentation(4);

// Après
$yaml = new Dumper(4);
# Avant
framework:
    secret:       %secret%
    router:
        resource: %kernel.root_dir%/config/routing.yml
# Après
framework:
    secret:       '%secret%'
    router:
        resource: '%kernel.root_dir%/config/routing.yml'

Source: https://symfony.com/blog/new-in-symfony-3-1-yaml-deprecations

3.1: Configuration du composant Yaml

Introduction de flags de configuration.

Dumper : * `Yaml::DUMP_OBJECT` * `Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE` * `Yaml::DUMP_OBJECT_AS_MAP` * `Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK`

Parser : * `Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE` * `Yaml::PARSE_OBJECT` * `Yaml::PARSE_OBJECT_FOR_MAP` * `Yaml::PARSE_DATETIME`

Source: https://symfony.com/blog/new-in-symfony-3-1-customizable-yaml-parsing-and-dumping

3.1: Composant Cache PSR-6

// filesystem, apcu, redis, array, doctrine cache, etc.
$cache = new Symfony\Component\Cache\Adapter\FilesystemAdapter();

$numProducts = $cache->getItem('stats.num_products');
$numProducts->set(4711);
$cache->save($numProducts);

$numProducts = $cache->getItem('stats.num_products');
if (!$numProducts->isHit()) {
    // Doesn't exist in the cache!
}
$total = $numProducts->get();

$cache->deleteItem('stats.num_products');
Source: https://symfony.com/blog/new-in-symfony-3-1-cache-component

3.1: Autres

* Amélioration du composant Ldap * Amélioration des traductions au format XLIFF * Optimisations des composants DoctrineBridge, Serializer, Process * Création de l'event `kernel.controller_arguments` * Dépréciation de `AbstractFormLoginAuthenticator::onAuthenticationSuccess()` * Dépréciation des chemins de templates absolus * Dépréciation de `choices_as_values` dans les `FormType`

# Symfony 3.2

3.2: Méthode `->file()` dans le contrôleur

public function downloadAction()
{
    $file = new \SplFileInfo('/var/tmp/processed_file');
    // Avant
    $response = new BinaryFileResponse($file);
    $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'new_filename.pdf');

    // Après
    return $this->file($file, 'new_filename.pdf');
}
Source: https://symfony.com/blog/new-in-symfony-3-2-file-controller-helper

3.2: Dépréciations YAML

Espaces manquants après `:`

# Avant
foo:bar
default_page:1

# Après
foo: bar
default_page: 1

Clés dupliquées

# Avant
foo: bar
foo: baz
# Résolu comme ['foo' => 'bar'] ("baz" était ignoré)
Source: https://symfony.com/blog/new-in-symfony-3-2-yaml-deprecations

3.2: Constantes PHP dans les fichiers YAML

parameters:
    # Resolved as a simple int
    foo: PHP_INT_MAX

    # Resolved as the PHP constant's value
    bar: !php/const:PHP_INT_MAX
Source: https://symfony.com/blog/new-in-symfony-3-2-php-constants-in-yaml-files

3.2: Meilleure lisibilité pour les nombres en Yaml

# Avant
parameters:
    credit_card_number: 1234567890123456
    long_number: 10000000000
    pi: 3.141592653589793
    hex_words: 0xCAFEF00D

# Après
parameters:
    credit_card_number: 1234_5678_9012_3456
    long_number: 10_000_000_000
    pi: 3.14159_26535_89793
    hex_words: 0x_CAFE_F00D
Source: https://symfony.com/blog/new-in-symfony-3-2-better-readability-for-yaml-numeric-literals

3.2: Amélioration des passes de compilation

Ajouté une priorité :

public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0);

Tri des services tagués par un champ `priority` :

services:
    App\Test\MonthlyTest:
        tags:
            - { name: app.test, priority: 2 }
    App\Test\DailyTest:
        tags:
            - { name: app.test, priority: 1 }
class CustomPass implements CompilerPassInterface
{
    use PriorityTaggedServiceTrait;

    public function process(ContainerBuilder $container)
    {
        $tests = $this->findAndSortTaggedServices('app.test', $container);

        // ...
    }
}

Source: https://symfony.com/blog/new-in-symfony-3-2-compiler-passes-improvements

3.2: `DateIntervalType`

$builder
    ->add('remindEvery', DateIntervalType::class)
;
Source: https://symfony.com/blog/new-in-symfony-3-2-dateinterval-form-type

3.2: Tags dans le cache

$review = $cacheAdapter->getItem('reviews-'.$reviewId);

$review->set('...');

$review->tag(['reviews', 'products', 'product-'.$productId]);

$cache->invalidateTags('products');

$cache->invalidateTags('product-123');

$cache->invalidateTags(['products', 'reviews']);
Source: https://symfony.com/blog/new-in-symfony-3-2-tagged-cache

3.2: Alias de commandes

Les alias ne sont plus affichés comme des commandes à part entière.

$ bin/console list


# Avant
Available commands:
    foo                      Lorem Ipsum...  (ceci est un alias...)
  app:
    app:very:long:name       Lorem Ipsum...


# Après
Available commands:
  app:
    app:very:long:name       [foo] Lorem Ipsum...
Source: https://symfony.com/blog/new-in-symfony-3-2-console-improvements-part-1

3.2: Améliorations de la console

* Même avec l'option `-q`, les erreurs s'affichent désormais. * Nouveau paramètre dans `Application::setDefaultCommand('name', true)` pour les applications mono-commandes. * Une interface `StreamableInputInterface` pour faciliter le flux d'entrée en console. * Un trait `LockableTrait` pour lock une exécution. * Commandes masquées dans `bin/console list`.

Sources: * https://symfony.com/blog/new-in-symfony-3-2-console-improvements-part-1 * https://symfony.com/blog/new-in-symfony-3-2-console-improvements-part-2 * https://symfony.com/blog/new-in-symfony-3-2-console-improvements-part-3

3.2: Améliorations du `CommandTester`

// Avant

use Symfony\Component\Console\Tester\CommandTester;

$commandTester = new CommandTester($command);
$helper = $command->getHelper('question');
$helper->setInputStream($this->getInputStream("123\nfoo\nbar\n"));

protected function getInputStream($input)
{
    $stream = fopen('php://memory', 'r+', false);
    fputs($stream, $input);
    rewind($stream);
    return $stream;
}
// Après

use Symfony\Component\Console\Tester\CommandTester;

$commandTester = new CommandTester($command);
$commandTester->setInputs(['123', 'foo', 'bar'])

Source: https://symfony.com/blog/new-in-symfony-3-2-console-improvements-part-1

3.2: Console : la phase `Terminal`

class Terminal
{
    public function getWidth();
    public function getHeight();
}
Source: https://symfony.com/blog/new-in-symfony-3-2-console-improvements-part-2

3.2: D e s c o u l e u r s e n c o n s o l e !

$output->writeln('<fg=green;options=bold,underscore>Test</>');
Source: https://symfony.com/blog/new-in-symfony-3-2-console-improvements-part-3

3.2: Chargement _lazy_ pour les `ChoiceType`

$builder->add('constants', ChoiceType::class, [
    'choice_loader' => new CallbackChoiceLoader(function() {
            return StaticClass::getConstants();
    },
]);

$builder->add('values_from_another_world', ChoiceType::class, [
    'choice_loader' => new CallbackChoiceLoader(function() use ($externalService) {
            return $externalService->fetchChoicesValues();
    },
]);
Source: https://symfony.com/blog/new-in-symfony-3-2-lazy-loading-of-form-choices

3.2: Injection de l'utilisateur dans les contrôleurs

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\User\UserInterface;

class DefaultController extends Controller
{
    // Quand l'utilisateur est obligatoire
    public function fooAction(UserInterface $user) { ... }

    // Quand l'utilisateur est facultatif (recommandé, dans le doute)
    public function barAction(UserInterface $user = null) { ... }
}
Source: https://symfony.com/blog/new-in-symfony-3-2-user-value-resolver-for-controllers

3.2: Cookies `same-site`

$cookie = new Cookie(..., Cookie::SAMESITE_LAX);
$cookie = new Cookie(..., Cookie::SAMESITE_STRICT);
Source: https://symfony.com/blog/new-in-symfony-3-2-httpfoundation-improvements

3.2: En-têtes de cache corrigées

* Les réponses de code 301 n'ajoutent plus automatiquement l'en-tête `no-cache`. * Les réponses avec l'en-tête `no-cache` se voient ajouter `private` si ce n'est pas précisé.

Source: https://symfony.com/blog/new-in-symfony-3-2-httpfoundation-improvements

3.2: Méthodes _idempotent_

$request->isMethodIdempotent();
Source: https://symfony.com/blog/new-in-symfony-3-2-httpfoundation-improvements

3.2: Composant Workflow

Source: https://symfony.com/doc/current/components/workflow.html

3.2: Services privés améliorés

services:
    app.manager.user:
        class: AppBundle\Manager\User
        public: false
Source: https://symfony.com/blog/new-in-symfony-3-2-improved-private-services

3.2: Liens symboliques avec `Filesystem`

public function readlink($path, $canonicalize = false);
public function hardlink($originFile, string|array $targetFiles);
Source: https://symfony.com/blog/new-in-symfony-3-2-filesystem-improvements

3.2: Runtime Environment Variables

parameters:
    # Utilisation :
    database_url: '%env(DATABASE_URL)%'

    # Valeur par défaut si la variable d'env n'est pas définie
    env(DATABASE_URL): 'mysql://db_user:db_password@127.0.0.1:3306/db_name'
Source: https://symfony.com/blog/new-in-symfony-3-2-runtime-environment-variables

3.2: Encodeur CSV dans le Serializer

$data = [
    'foo' => 'aaa',
    'bar' => [
        ['id' => 111, 1 => 'bbb'],
        ['lorem' => 'ipsum'],
    ]
];

$csvString = $container->get('serializer')->encode($data, 'csv');
foo,bar.0.id,bar.0.1,bar.1.lorem
aaa,111,bbb,ipsum
Source: https://symfony.com/blog/new-in-symfony-3-2-csv-and-yaml-encoders-for-serializer

3.2: Encodeur Yaml dans le Serializer

$obj = new \stdClass();
$obj->bar = 2;

$yamlString = $this->container->get('serializer')->encode(
    [
        'foo' => $obj,
        'bar' => 'Such wow',
    ],
    'yaml',
    ['yaml_inline' => 1, 'yaml_indent' => 4, 'yaml_flags' => Yaml::DUMP_OBJECT]
);
foo: !php/object 'O:8:"stdClass":1:{s:3:"bar";i:2;}'
bar: 'Such wow'
Source: https://symfony.com/blog/new-in-symfony-3-2-csv-and-yaml-encoders-for-serializer

3.2: Nouveautés du Cache

* `Symfony\Component\Cache\Adapter\NullAdapter` * `Symfony\Component\Cache\Adapter\PhpFilesAdapter` * `Symfony\Component\Cache\Adapter\PdoAdapter` * `Symfony\Component\Cache\Adapter\TagAwareAdapter` * Commande `cache:pool:clear`

Source: https://symfony.com/blog/new-in-symfony-3-2-cache-improvements

3.2: Config du Firewall exposée

* `Symfony\Bundle\SecurityBundle\Security\FirewallConfig` * La config du firewall est aussi affichée dans la debug toolbar & le profiler

Source: https://symfony.com/blog/new-in-symfony-3-2-firewall-config-class-and-profiler

3.2: DX

* `$progress->advance()` permet les valeurs négatives. * `framework.ide: phpstorm` pour des liens de debug * Constructeur statique `JsonResponse::fromJsonString($jsonString)`

Source: https://symfony.com/blog/new-in-symfony-3-2-dx-improvements

3.2: Autres améliorations

* Moins de dépendances dans le FrameworkBundle * Un _dumper_ AST pour ExpressionLanguage * Perfs accrues avec un chargement sélectif pour les extensions Twig

Source: https://symfony.com/blog/new-in-symfony-3-2-misc-improvements

# Symfony 3.3

3.3: FQCN pour le nom d'un service

services:
    # Avant
    app.manager.user:
        class: AppBundle\EventListener\UserManager
        tags:  ['kernel.event_subscriber']

    # Après
    AppBundle\EventListener\UserManager:
        tags:  ['kernel.event_subscriber']
// Avant
$this->get('app.manager.user')->save($user);

// Après
$this->get(UserManager::class)->save($user);

Source: https://symfony.com/blog/new-in-symfony-3-3-optional-class-for-named-services

3.3: Arguments de service

# Avant
services:
    App\Foo\Bar:
        arguments: ['@baz', 'foo', '%qux%']

# Après
services:
    App\Foo\Bar: ['@baz', 'foo', '%qux%']
Source: https://symfony.com/blog/new-in-symfony-3-3-simpler-service-configuration

3.3: Auto-configuration de services

services:
    _instanceof:
        Symfony\Component\Console\Command\Command:
            tags: ['console.command']
            public: true

        Twig_ExtensionInterface:
            tags: ['twig.extension']

        Symfony\Component\EventDispatcher\EventSubscriberInterface:
            tags: ['kernel.event_subscriber']

    App\EventListener\MyListener:
        autoconfigure: true
Source: https://symfony.com/blog/new-in-symfony-3-3-service-autoconfiguration

3.3: Config par défaut de services

services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false
Source: https://symfony.com/blog/new-in-symfony-3-3-simpler-service-configuration

3.3: Découverte de services via PSR-4

services:
    App\:
        resource: ../src/{Command}
    App\:
        resource: ../src/{Controller}
        public: true
Source: https://symfony.com/blog/new-in-symfony-3-3-psr-4-based-service-discovery

3.3: Nouveau design des pages d'exceptions

![Redesign exception](/img/exceptions_redesign.jpg)

Source: https://symfony.com/blog/new-in-symfony-3-3-redesigned-exception-pages

3.3: Routing plus rapide

Avec 900 routes, on passe de 7.5ms à 2.5ms. De façon générale, c'est entre 30% et 340% plus rapide.

Source: https://symfony.com/blog/new-in-symfony-3-3-faster-routing

3.3: A simpler way to get the project root directory

`%kernel.root_dir%` désormait désuet : place à `%kernel.project_dir%` !

Source: https://symfony.com/blog/new-in-symfony-3-3-a-simpler-way-to-get-the-project-root-directory

3.3: Messages flash

{# Avant #}

{% for label, messages in app.session.flashbag.all %}{% endfor %}

{% for message in app.session.flashbag.get('notice') %}{% endfor %}



{# Après #}

{% for label, messages in app.flashes %}{% endfor %}

{% for message in app.flashes('notice') %}{% endfor %}
Source: https://symfony.com/blog/new-in-symfony-3-3-improved-flash-messages

3.3: Dépréciation des variables d'environnement `SYMFONY__`

Avant : `SYMFONY__APP__CHARSET` définit le paramètre `%app.charset%` Après : On utilise des variables d'environnement 😉

Source: https://symfony.com/blog/new-in-symfony-3-3-deprecated-the-special-symfony-environment-variables

3.3: Commande `about`

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ ./bin/console about

 -------------------- ------------------------------------------
  Symfony
 -------------------- ------------------------------------------
  Version              3.3.16
  End of maintenance   01/2018 Expired
  End of life          07/2018
 -------------------- ------------------------------------------
  Kernel
 -------------------- ------------------------------------------
  Type                 AppKernel
  Name                 app
  Environment          dev
  Debug                true
  Charset              UTF-8
  Root directory       .\app
  Cache directory      ./var/cache/dev (558 KiB)
  Log directory        ./var/logs (0 B)
 -------------------- ------------------------------------------
  PHP
 -------------------- ------------------------------------------
  Version              7.2.2
  Architecture         64 bits
  Intl locale          fr_FR
  Timezone             Europe/Paris (2018-02-21T21:20:23+01:00)
  OPcache              true
  APCu                 true
  Xdebug               true
 -------------------- ------------------------------------------
Source: https://symfony.com/blog/new-in-symfony-3-3-about-command

3.3: Versioning d'assets avec un manifest

{
    "css/app.css": "build/css/app.b916426ea1d10021f3f17ce8031f93c2.css",
    "js/app.js": "build/js/app.13630905267b809161e71d0f8a0c017b.js"
    "...": "..."
}
# app/config/config.yml
framework:
    # ...
    assets:
        json_manifest_path: '%kernel.project_dir%/build/manifest.json'
Source: https://symfony.com/blog/new-in-symfony-3-3-manifest-based-asset-versioning

3.3: Recherches de fichiers de config "glob"

protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader)
{
    $confDir = dirname($this->getRootDir()).'/etc';
    $loader->import($confDir.'/packages/*.yaml', 'glob');
    $loader->import($confDir.'/packages/'.$this->getEnvironment().'/**/*.yaml', 'glob');
    $loader->import($confDir.'/container.yaml', 'glob');
}
imports:
    - { resource: '../common/' }
    - { resource: 'acme/*.yaml' }

Sources: * https://symfony.com/blog/new-in-symfony-3-3-load-config-files-with-glob-patterns * https://symfony.com/blog/new-in-symfony-3-3-import-config-files-with-glob-patterns

3.3: Méthode `Kernel::build()`

`Kernel::build()` fonctionne comme `Bundle::build()`

Source: https://symfony.com/blog/new-in-symfony-3-3-kernel-build-method

3.3: Conteneur PSR-11 & _service locators_

interface ContainerInterface
{
    public function get($id);
    public function has($id);
}
services:
    app.command_handler_locator:
        class: Symfony\Component\DependencyInjection\ServiceLocator
        tags: ['container.service_locator']
        arguments:
            -
                AppBundle\FooCommand: '@app.command_handler.foo'
                AppBundle\BarCommand: '@app.command_handler.bar'

    AppBundle\CommandBus:
        arguments: ['@app.command_handler_locator']

Sources: * https://symfony.com/blog/new-in-symfony-3-3-psr-11-containers * https://symfony.com/blog/new-in-symfony-3-3-service-locators

3.3: `AbstractController`

`Symfony\Bundle\FrameworkBundle\Controller\Controller` devient `Symfony\Bundle\FrameworkBundle\Controller\AbstractController`

3.3: Cache PSR-16 minimaliste

use Symfony\Component\Cache\Simple\FilesystemCache;

$cache = new FilesystemCache();

$cache->set('stats.num_products', 4711); // Save

if (!$cache->has('stats.num_products')) { // Fetch
    // ... item does not exist in the cache
} else {
    $total = $cache->get('stats.num_products');
}

$cache->delete('stats.num_products');
Source: https://symfony.com/blog/new-in-symfony-3-3-simple-cache

3.3: Composant Dotenv

# .env

DATABASE_URL="mysql://root@127.0.0.1/app_db?serverVersion=5.7"
use Symfony\Component\Dotenv\Dotenv;

(new Dotenv())->load(__DIR__.'/.env');

$dbPassword = $_ENV['DATABASE_URL'];
Source: https://symfony.com/blog/new-in-symfony-3-3-dotenv-component

3.3: WebServerBundle

Surtout pour DX et pour faciliter l'installation du bundle en dehors de `symfony/symfony`

Source: https://symfony.com/blog/new-in-symfony-3-3-webserverbundle

3.3: Quelques raccourcis DX

// Avant
$container->register('app.twig_extension', AppExtension::class)
    ->setAutowired(true)
;

// Après
$container->autowire('app.twig_extension', AppExtension::class)
$node = new ArrayNodeDefinition('name');

// Avant
$node->prototype('integer')->max(10);

// Après
$node->integerPrototype()->max(10);
services:
    AppBundle\Twig\AppExtension
        # Avant
        tags:
            - { name: twig.extension }

        # Après
        tags: ['twig.extension']
Source: https://symfony.com/blog/new-in-symfony-3-3-added-new-shortcut-methods

3.3: Infos sur Symfony dans le _profiler_

![profiler upgrade](/img/profiler_1.jpg)

![profiler upgrade](/img/profiler_2.jpg) ![profiler upgrade](/img/profiler_3.jpg)

Source: https://symfony.com/blog/new-in-symfony-3-3-improved-the-profiler-configuration-panel

3.3: Authentification JSON

security:
    firewalls:
        main:
            json_login:
                check_path: /login
GET /login HTTP/1.1
Host: localhost

{ "username": "foo", "password": "bar1234" }
Source: https://symfony.com/blog/new-in-symfony-3-3-json-authentication

# Symfony 3.4 (LTS)

3.4: Composant Lock

$lock = $lockFactory->createLock('pdf-invoice-generation');

if ($lock->acquire()) {
    // Do stuff

    $lock->release();
}
$lock->isAcquired();
$lock->refresh();
$lock->release();
Source: https://symfony.com/blog/new-in-symfony-3-3-lock-component

3.4: Commande `debug:autowiring`

$ ./bin/console debug:autowiring

Autowirable Services
====================

The following classes & interfaces can be used as type-hints when autowiring:

  App\Command\AddUserCommand
  App\Command\DeleteUserCommand
  App\Command\ListUsersCommand
  App\Controller\Admin\BlogController
  App\Controller\BlogController
  App\Controller\SecurityController
  App\EventSubscriber\CheckRequirementsSubscriber
  App\EventSubscriber\CommentNotificationSubscriber
Source: https://symfony.com/blog/new-in-symfony-3-4-debug-autowiring-command

3.4: Désactiver les form themes globaux

{% form_theme form with ['common.html.twig', 'form/fields.html.twig'] only %}
Source: https://symfony.com/blog/new-in-symfony-3-4-disable-global-form-themes

3.4: Dépréciations & améliorations du Guard

* Dépréciations de `GuardAuthenticatorInterface` au profit de `AuthenticatorInterface`. * `getCredentials()` doit soit renvoyer des credentials, soit une exception. * Ajout d'une méthode `supports()` pour compenser le changement de `getCredentials()`.

Source: https://symfony.com/blog/new-in-symfony-3-4-guard-authentication-improvements

3.4: Commande `debug:form`

$ ./bin/console debug:form

Built-in form types (Symfony\Component\Form\Extension\Core\Type)
----------------------------------------------------------------
 BirthdayType, ButtonType, CheckboxType, ChoiceType, CollectionType
 ...
 TextareaType, TimeType, TimezoneType, UrlType

Service form types
------------------
 * App\Form\CommentType
 * ...
 * Symfony\Bridge\Doctrine\Form\Type\EntityType

Type extensions
---------------
 * Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension
 * ...

Type guessers
-------------
 * Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser
 * ...
Source: https://symfony.com/blog/new-in-symfony-3-4-debug-form-command

3.4: _"Service bindings"_

# Avant
services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false

    App\Some\Service1:
        $projectDir: '%kernel.project_dir%'

    App\Some\Service2:
        $projectDir: '%kernel.project_dir%'

    App\Some\Service3:
        $projectDir: '%kernel.project_dir%'
# Après
services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false
        bind:
            $projectDir: '%kernel.project_dir%'

    App\Some\Service1: ~
    App\Some\Service2: ~
    App\Some\Service3: ~

Source: https://symfony.com/blog/new-in-symfony-3-4-local-service-binding

3.4: Services privés par défaut

Source: https://symfony.com/blog/new-in-symfony-3-4-services-are-private-by-default

3.4: Logs PSR-3 intégrés au framework

* Logger minimaliste, toujours dispo, avec le nom de service `logger`. * Pas besoin d'installer d'autre lib de logs. * Autowiring possible via `Psr\Log\LoggerInterface`. * Écrit sur `php://stderr` par défaut. * `level: warning` par défaut. * Même API que Monolog

Source: https://symfony.com/blog/new-in-symfony-3-4-minimalist-psr-3-logger

3.4: Surcharge de templates de bundles

Avant : `app/Resources/TwigBundle/views/Exception/error404.html.twig` Après : `templates/bundles/TwigBundle/Exception/error404.html.twig` Rajout de `@!` pour forcer l'utilisation du template original non surchargé : `{% extends '@!EasyAdmin/layout.html.twig' %}`

Source: https://symfony.com/blog/new-in-symfony-3-4-improved-the-overriding-of-templates

3.4: Injection de services tagués

services:
    App\Manager\TwigManager:
        arguments: [!tagged twig.extension]
namespace App\Manager;

class TwigManager
{
    public function __construct(iterable $twigExtensions)
    {
        // ...
    }
}
Source: https://symfony.com/blog/new-in-symfony-3-4-simpler-injection-of-tagged-services

3.4: Auto-configuration du Kernel

Permet au kernel d'implémenter `EventSubscriberInterface` et `CompilerPassInterface`.

Sources: * https://symfony.com/blog/new-in-symfony-3-4-simpler-injection-of-tagged-services * https://symfony.com/blog/new-in-symfony-3-4-subscribing-to-events-in-the-micro-kernel

3.4: Form theme pour Bootstrap 4

![Bootstrap form](/img/bootstrap_form.jpg)

Source: https://symfony.com/blog/new-in-symfony-3-4-bootstrap-4-form-theme

3.4: Encoder Argon2i

security:
    encoders:
        Symfony\Component\Security\Core\User\User:
            algorithm: 'argon2i'
Source: https://symfony.com/blog/new-in-symfony-3-4-argon2i-password-hasher

3.4: Contexte de requête pour les assets

asset.request_context.base_path: '/subfolder'
asset.request_context.secure: true
{{ asset('/foo/image.jpg') }}
{# /subfolder/foo/image.jpg #}
Source: https://symfony.com/blog/new-in-symfony-3-4-default-request-context-for-assets

3.4: Dépréciation de l'héritage de bundles

Source: https://symfony.com/blog/new-in-symfony-3-4-deprecated-bundle-inheritance

3.4: Config PHP _fluent_ pour les services

return function (ContainerConfigurator $container) {
    $container->import('legacy_services.php');

    $params = $container->parameters();
    $params->set('app.foo_param', 'param_value');

    $container = $container->services()->defaults()
        ->private()
        ->autoconfigure()
        ->autowire();

    $container->load('App\\', '../src/*')
        ->exclude('../src/{Entity,Repository,Tests}');

    $container->load('App\\Controller\\', '../src/Controller')
        ->tag('controller.service_arguments');

    $container->alias('foo', FooClass::class)->public();
};
Source: https://symfony.com/blog/new-in-symfony-3-4-php-based-configuration-for-services-and-routes

3.4: Config PHP _fluent_ pour les routes

return function (RoutingConfigurator $routes) {
    $routes->import('legacy_routes.php')
        ->prefix('/legacy')
    ;

    $routes->add('product', '/products/{id}')
        ->controller('App\Controller\ProductController::show')
        ->schemes(['https'])
        ->requirements(['id' => '\d+'])
        ->defaults(['id' => 0]);

    $routes->add('homepage', '/')
        ->controller('App\Controller\DefaultController::index');
};
Source: https://symfony.com/blog/new-in-symfony-3-4-php-based-configuration-for-services-and-routes

3.4: Variables d'environnement avancées

parameters:
    project_dir: '/foo/bar'
    env(DB): 'sqlite://%%project_dir%%/var/data.db'
    db_dsn: '%env(resolve:DB)%'

    app.connection.port: '%env(int:DATABASE_PORT)%'

    env(SECRETS_FILE): '/etc/secure/example.com/secrets.json'
    app.secrets: '%env(json:file:SECRETS_FILE)%'

    env(SOME_VALUE): 'NWE3OWExYzg2NmVmZWY5Y2ExODAwZjk3MWQ2ODlmM2U='
    app.some_value: '%env(base64:SOME_VALUE)%'

    env(NUM_ITEMS): 'App\\Entity\\BlogPost::NUM_ITEMS'
    app.num_items: '%env(constant:NUM_ITEMS)%'
Source: https://symfony.com/blog/new-in-symfony-3-4-advanced-environment-variables

3.4: Ajout de `propertyPath` dans les contraintes

/*
 * Avant
 * @Assert\Expression("value > this.startDate")
 *
 * Après
 * @Assert\GreaterThan(propertyPath="startDate")
 */
Source: https://symfony.com/blog/new-in-symfony-3-4-improved-comparison-constraints

3.4: Préfixe de nom pour les routes d'un contrôleur

use Symfony\Component\Routing\Annotation\Route;

/** @Route("/blog", name="blog_") */
class BlogController extends Controller
{
    /**
     * @Route("/", defaults={"page": "1"}, name="index")
     * @Route("/page/{page}", name="index_paginated")
     */
    public function indexAction($page, $_format) { ... }

    /**
     * @Route("/posts/{slug}", name="post")
     */
    public function showAction(Post $post) { ... }
}

Routes : `blog_index`, `blog_index_paginated`, `blog_post`.

Source: https://symfony.com/blog/new-in-symfony-3-4-prefix-all-controller-route-names

3.4: Commandes chargées en mode _lazy_

Dans la config

app.command.complex_command:
    tags:
        - { name: console.command, command: app:my-command }

Dans la classe directement

use Symfony\Component\Console\Command\Command;

class MySuperCommand extends Command
{
    protected static $defaultName = 'app:my-command';

    // ...
}
Source: https://symfony.com/blog/new-in-symfony-3-4-lazy-commands

3.4: Validator dans le profiler

![Redesign exception](/img/validator_profiler_1.png)

Source: https://symfony.com/blog/new-in-symfony-3-4-validator-information-in-the-symfony-profiler

# Symfony 4.0

# Symfony 4.1

Mais au fait...

Comment on fait pour savoir ce qu'il y a de nouveau dans Symfony 4.1?

⇒ http://symfony.com/blog/category/living-on-the-edge

⇒ http://github.com/pierstoval/livingedge

# Flex

Avant Flex

C'est quoi Flex ?

Un plugin Composer ([symfony/flex](https://packagist.org/packages/symfony/flex))

Le serveur Flex ([symfony.sh](https://symfony.sh))

Les recettes (_recipes_) officielles ([symfony/recipes](https://github.com/symfony/recipes))

Les recettes (_recipes_) "contrib" ([symfony/recipes-contrib](https://github.com/symfony/recipes-contrib))

![](/img/eminem_wrong.gif)

Comment on s'en sert ?

composer require symfony/flex

Qu'est-ce que ça cache ?

![](/img/malabar.png)

Qu'est-ce que ça ~~cache~~ apporte ?

├── app
│   ├── config
│   └── Resources
│       └── views
│           └── default
├── bin
├── src
│   └── AppBundle
│       └── Controller
├── tests
│   └── AppBundle
│       └── Controller
├── var
│   ├── cache
│   ├── logs
│   └── sessions
├── vendor
└── web
</pre>
├── bin
├── config
│   └── packages
│       ├── dev
│       └── test
├── public
├── src
│   └── Controller
├── var
│   ├── cache
│   └── log
└── vendor

Qu'est-ce que ça apporte d'autre ?

$ composer req server api admin orm behat cs-fixer cors annot twig

Mais encore ?

$ composer req annot
Using version ^5.1 for sensio/framework-extra-bundle
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 7 installs, 0 updates, 0 removals
  - Installing doctrine/lexer (v1.0.1): Loading from cache
  - Installing doctrine/inflector (v1.2.0): Loading from cache
  - Installing doctrine/collections (v1.5.0): Loading from cache
  - Installing doctrine/cache (v1.7.1): Loading from cache
  - Installing doctrine/annotations (v1.5.0): Loading from cache
  - Installing doctrine/common (v2.8.1): Loading from cache
  - Installing sensio/framework-extra-bundle (v5.1.2): Loading from cache
Writing lock file
Generating autoload files
Symfony operations: 2 recipes (3ac0e3828e0167b379d5284080768bc5)
  - Configuring doctrine/annotations (>=1.0): From github.com/symfony/recipes:master
  - Configuring sensio/framework-extra-bundle (>=4.0): From github.com/symfony/recipes:master
Executing script cache:clear [OK]
Executing script assets:install --symlink --relative public [OK]

Diff :

diff --git a/config/bundles.php b/config/bundles.php
index 49d3fb6..0d71512 100644
--- a/config/bundles.php
+++ b/config/bundles.php
@@ -2,4 +2,5 @@

 return [
     Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
+    Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
 ];
diff --git a/config/routes/annotations.yaml b/config/routes/annotations.yaml
new file mode 100644
index 0000000..d49a502
--- /dev/null
+++ b/config/routes/annotations.yaml
@@ -0,0 +1,3 @@
+controllers:
+    resource: ../../src/Controller/
+    type: annotation

Recettes

{
    "bundles": {
        "Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle": ["all"]
    },
    "copy-from-recipe": {
        "config/": "%CONFIG_DIR%/",
        "public/": "%PUBLIC_DIR%/",
        "src/": "%SRC_DIR%/"
    },
    "composer-scripts": {
        "cache:clear": "symfony-cmd",
        "assets:install --symlink --relative %PUBLIC_DIR%": "symfony-cmd"
    },
    "env": {
        "APP_ENV": "dev",
        "APP_SECRET": "%generate(secret)%",
        "#TRUSTED_PROXIES": "127.0.0.1,127.0.0.2",
        "#TRUSTED_HOSTS": "localhost,example.com"
    },
    "gitignore": [
        ".env",
        "/public/bundles/",
        "/var/",
        "/vendor/"
    ]
}

Création d'un projet

$ composer create symfony/website-skeleton monprojet
# ...
$ composer req admin api
# ...
$ php bin/console make:controller DefaultController
$ php bin/console make:functional-test DefaultControllerTest
$ php bin/console make:entity User
$ php bin/console make:entity Post
$ php bin/console make:command UpdateCommentsStatsCommand
$ php bin/console make:voter BlogPostVoter
$ php bin/console make:auth FormLoginAuthenticator
$ php bin/console make:auth OAuthAuthenticator
# ...
# ...
# Start coding !

Demo

Merci !





@pierstoval