Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[SymfonyLive Paris 2022] Des composants Symfony méconnus qui valent le détour

[SymfonyLive Paris 2022] Des composants Symfony méconnus qui valent le détour

Plus qu'un framework, Symfony est un écosystème. Les composants Symfony, véritables bibliothèques indépendantes utilisables dans n'importe quel projet PHP, proposent de régler des problématiques que nous sommes susceptibles de rencontrer quotidiennement en tant que développeurs PHP.
Manipulation de chaînes de caractères avec Unicode, concurrence, internationalisation ou encore rate-limiting sont, entre autres, tous autant de défis que les composants Symfony permettent de régler avec des solutions clé en main. Et tout ça, en profitant de la robustesse d'un code open-source et de la fameuse promesse de rétrocompatibilité.

Bien que certains composants soient connus et reconnus, il en existe au moins tout autant qui pourraient vous sauver de longues heures de développement si vous connaissiez leur existence. C'est ce que nous allons explorer ensemble.

Alexandre Daubois

April 08, 2022
Tweet

More Decks by Alexandre Daubois

Other Decks in Technology

Transcript

  1. ALEXANDRE DAUBOIS Développeur Symfony chez SensioLabs Certifié Symfony 6.0 (Expert

    Level) Auteur sur Medium Contributeur à Symfony @alexdaubois 2
  2. HTML PURIFIER GARDER AU MIEUX L'ARBORESCENCE ORIGINALE DES NOEUDS SUPPRIMER

    LES DONNÉES POTENTIELLEMENT DANGEREUSES TOP POUR DES DOCUMENTS STRUCTURÉS 12
  3. HTML SANITIZER RECONSTRUIRE DU HTML EN EXTRAYANT LES DONNÉES SAFE

    DE L'INPUT LA STRUCTURE DE BASE PEUT ÊTRE PERDUE TOP POUR DU WYSIWYG PAR EXEMPLE 13
  4. HTMLSANITIZERCONFIG use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig; $config = (new HtmlSanitizerConfig()) // Autoriser les

    élements 'h1' dans la sortie ->allowElement('h1') // Retirer les élements 'h2' de la sortie mais garder leurs descendants ->blockElement('h2') // Supprimer les élements 'h3' de la sortie ainsi que l'intégralité de leurs descendants ->dropElement('h3'); 1 2 3 4 5 6 7 8 9 14
  5. HTMLSANITIZERCONFIG use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig; $config = (new HtmlSanitizerConfig()) // Autoriser les

    attributs 'data-text' des élements 'h1' ->allowAttribute('data-text', ['h1']) // Retirer les attributs 'data-text' des élements 'h2' ->blockAttribute('data-text', ['h2']) // Forcer tous les liens à s'ouvrir dans un nouvel onglet ->forceAttribute('a', '_target', 'blank'); 1 2 3 4 5 6 7 8 9 15
  6. HTMLSANITIZERCONFIG FORCER HTTPS SUR LES URLS ALLOWED LINK SCHEMES /

    ALLOWED MEDIA SCHEMES ALLOWED LINK HOSTS / ALLOWED MEDIA HOSTS ET BIEN D'AUTRES! 16
  7. HTMLSANITIZER use Symfony\Component\HtmlSanitizer\HtmlSanitizer; $config = ...; $sanitizer = new HtmlSanitizer($config);

    // Context unaware $sanitizer->sanitize($input); /** * Context aware, parmi : * - W3CReference::CONTEXT_HEAD / 'head' * - W3CReference::CONTEXT_BODY / 'body' * - W3CReference::CONTEXT_TEXT / 'text' */ $sanitizer->sanitizeFor($context, $input); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  8. W3CREFERENCE DÉFINITION DES CONTEXTES (HEAD, BODY ET TEXT) ÉLÉMENTS ET

    ATTRIBUTS AUTORISÉS POUR CHAQUE CONTEXTE POUR CHAQUE ÉLÉMENT ET ATTRIBUT : EST-IL SAFE ? 18
  9. 🙏 + "MEDIUM SKIN TONE" = Code point U+1F64F Code

    point U+1F3FD Grapheme cluster 28
  10. CRÉER DES CHAÎNES // Nouvelle instance de ByteString use function

    Symfony\Component\String\b; $foo = new ByteString('\xc4\xf5'); $foo = b('\xc3\xf5'); // Nouvelle instance de UnicodeString use function Symfony\Component\String\u; $foo = new UnicodeString('hello'); $foo = u('hello'); // Nouvelle instance de CodePointString $foo = new CodePointString('hello'); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 PAR CONSTRUCTEUR 29
  11. CRÉER DES CHAÎNES // Symfony/Component/String/Resources/functions.php namespace Symfony\Component\String; function u(?string $string

    = ''): UnicodeString { return new UnicodeString($string ?? ''); } function b(?string $string = ''): ByteString { return new ByteString($string ?? ''); } /** * @return UnicodeString|ByteString */ function s(?string $string = ''): AbstractString { $string ??= ''; return preg_match('//u', $string) ? new UnicodeString($string) : new ByteString($string); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 30
  12. MANIPULER DES CHAÎNES u('https://symfony.com')->startsWith('https'); // True u('iPhone 13 Pro')->endsWith('Pro'); //

    True u('https://twitter.com/alexdaubois')->containsAny('daubois'); // True u('https://github.com/alexandre-daubois')->replace('alexandre-daubois', 'sensiolabs'); // https://github.com/sensiolabs u('Symfony: le Framework')->camel(); // 'symfonyLeFramework' u('Symfony: le Framework')->snake(); // 'symfony_le_framework' u('SymfonyLive Paris 2022')->truncate(9, '…'); // 'SymfonyLi…' u('0123456789')->chunk(3); // ['012', '345', '678', '9'] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 32
  13. INTÉGRATION AVEC TWIG TWIG/STRING-EXTRA (INCLU DANS TWIG/EXTRA-BUNDLE) {# 1) Mise

    à disposition du filtre 'u' pour manipuler des UnicodeStrings ainsi que toutes leurs méthodes #} {{ 'Lorem ipsum'|u.truncate(8, '...') }} {# Lorem... #} {# 2) Mise à disposition du filtre 'slug' pour utiliser AsciiSlugger #} {{ 'SymfonyLive à Paris !'|slug }} {# symfonylive-a-paris #} 1 2 3 4 5 6 7 33
  14. EN BONUS... /** * Un slugger est aussi présent dans

    le composant */ $slugger = new AsciiSlugger(); $slug = $slugger->slug('Comment utiliser le composant String de Symfony ?'); // $slug = 'comment-utiliser-le-composant-string-de-symfony' /** * Embarque l'ancien composant Inflector */ $inflector = new EnglishInflector(); $result = $inflector->singularize('women'); // ['woman'] $result = $inflector->singularize('radii'); // ['radius'] $result = $inflector->pluralize('news'); // ['news'] $result = $inflector->pluralize('datum'); // ['data'] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ASCIISLUGGER & INFLECTOR 34
  15. AVEZ-VOUS CONFIANCE ? $myOptions = [ 'host' => 'live.symfony.com', 'port'

    => -4, 'shceme' => 'http', ]; $defaultOptions = [ 'host' => 'symfony.com', 'scheme' => 'https', 'port' => 12000, 'fragment' => '#anchor', ]; $fullOptions = array_replace($defaultOptions, $options); /** Output : array(5) { ["host"] => 🤩 string(11) "live.symfony.com" ["scheme"] => string(5) "https" ["port"] => 😢 int(-4) ["fragment"] => string(7) "#anchor" ["shceme"] => 😢 string(4) "http" } */ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 39
  16. OPTIONSRESOLVER REPLACE, OUI, MAIS PUISSANT ! Valeurs obligatoires Callbacks Validation

    et typage Dépendance de valeurs entre options Dépréciation d'options Normalization 42
  17. OPTIONSRESOLVER VALEURS PAR DÉFAUT, VALEURS OBLIGATOIRES $myOptions = ...; //

    Un tableau d'option défini par le développeur, par exemple $resolver = new OptionsResolver(); $resolver->setDefaults([ 'host' => 'symfony.com', 'scheme' => 'https', 'port' => function (Options $options) { if ('https' === $options['scheme']) { return 443; } return 80; }, ]); $resolver->setRequired(['fragment', 'protocol_version']); $options = $resolver->resolve($myOptions); // $options est un array 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 43
  18. D’AILLEURS… VALEURS PAR DÉFAUT : VOUS LES MANIPULEZ DÉJÀ !

    <?php namespace App\Form\Type; use App\Entity\User; use Symfony\Component\OptionsResolver\OptionsResolver; class UserType extends AbstractType { // ... public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'data_class' => User::class, ]); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 44
  19. OPTIONSRESOLVER VALIDATION ET OPTIONS DÉPRÉCIÉES $myOptions = ...; // Un

    tableau d'option défini par le développeur, par exemple $resolver = new OptionsResolver(); // ... $resolver->setAllowedTypes('host', 'string'); $resolver->setAllowedTypes('port', 'int'); // Vous pouvez aussi utiliser 'integer' $resolver->setDeprecated('port', 'phpusla/phpusla', '2.0', 'Specifying the port option is deprecated since 2.0. Use the scheme option instead.'); $options = $resolver->resolve($myOptions); // $options est un array résolu 1 2 3 4 5 6 7 8 9 10 11 45
  20. OPTIONSRESOLVER À PROPOS DE LA VALIDATION // Utilisation d'un FQCN

    $resolver->setAllowedTypes('host', Host::class); // Validation récursive de l'option $resolver->setAllowedTypes('query_parameters', 'string[]'); // Acceptation de plusieurs type (meilleure DX), mais... $resolver->setAllowedTypes('port', ['int', 'null', 'string']); 1 2 3 4 5 6 7 8 46
  21. ICU "INTERNATIONAL COMPONENT FOR UNICODE" BIBLIOTHÈQUE C/C++ & JAVA CONSORTIUM

    UNICODE COLLATOR, TIMEZONE, DATE FORMATTER, NUMBER FORMATTER, LOCALE... ET PLEIN D'AUTRES CHOSES ! 54
  22. INTL LE COMPOSANT DONNE ACCÈS À... Languages Scripts Countries Locales

    Currencies Time Zones Ça vous dit quelque chose ? 58
  23. INTL INTENSÉMENT UTILISÉ PAR LE COMPOSANT FORM LanguageType CountryType LocaleType

    CurrencyType TimezoneType Et dans Validator aussi ! 59
  24. INTL COMMENT L'UTILISER ? // Locale dans laquelle les résultats

    seront retournés \Locale::setDefault('fr'); Languages::getNames(); // array:595 [..., "am" => "amharique", "fro" => "ancien français", "tzm" => "amazighe de l’Atlas central", ...] Scripts::getNames(); /* array:199 [ ... "Sgnw" => "écriture des signes" "Zsye" => "emoji" "Egyp" => "hiéroglyphes égyptiens" "Maya" => "hiéroglyphes mayas" "Zxxx" => "non écrit" "Zmth" => "notation mathématique" ... ] */ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 60
  25. INTL COMMENT L'UTILISER ? Countries::getNames(); // [..., "FJ" => "Fidji",

    "FI" => "Finlande", "FR" => "France", ...] Locales::getNames(); // [..., "nl_SX" => "néerlandais (Saint-Martin [partie néerlandaise])", "nl_SR" => "néerlandais (Suriname)", "ne" => "népalais", ...] Currencies::getNames(); // [..., "EUR" => "euro", "CHE" => "euro WIR", "ANG" => "florin antillais", ...] Timezones::getNames(); // [..., "America/Dawson" => "heure normale du Yukon (Dawson)", "America/Whitehorse" => "heure normale du Yukon (Whitehorse)", "Etc/UTC" => "temps universel coordonné"] 1 2 3 4 5 6 7 8 9 10 11 61