alexandre

tl;dr : nous allons parler et illustrer l'intérêt de créer quelques objets PHP autour de certains composants externes, tout en restant sur l'écosystème Symfony, dans le but de réduire le travail lors des mises à jour du Framework.

Attention : cet article fait partie d'une série consacrée à l'architecture, les autres articles sont disponibles via cette recherche.

Préambule

J'ai vu passer un tweet de Grégoire hier concernant le changelog Symfony 6.3 à venir. En le parcourant, j'ai pu lire ceci :

Bundle/TwigBundle

Deprecate the Twig_Environment autowiring alias, use Twig\Environment instead

Ça m'a fait sourire parce que j'ai repensé à ce changement (le deprecate existe depuis très longtemps, Twig v2.7, juillet 2019).

Mais j'avais quand même ce sourire au coin qui dit “tiens, certaines personnes vont devoir faire avec cette dépréciation dans tout leur projet alors que moi, non”. Alors l'occasion fait le larron, petit article.

Pourquoi encapsuler ses dépendances externes ?

Si vous êtes tout neuf, ou toute neuve, sur des projets Symfony, vous ne savez peut être pas que le Framework a, et va, encore évoluer. Il va évoluer en termes de choix techniques, d'architecture, d'implémentation, de composants, etc. Il va même arriver certaines fois où l'écosystème Symfony va livrer un nouveau composant qui pourrait remplacer une dépendance tierce. C'est normal.

Les grandes questions sont donc : comment faire pour se prémunir de tout ça ? Comment éviter d'avoir du collatéral en cas de changement ? Comment passer moins de temps sur les migrations de code ? etc.

L'une des réponses : l'encapsulation.

Dans des architectures DDD, hexagonale, clean, “défensive” ou d'ailleurs, tout simplement en POO, on va vous inciter à vous protéger de vos dépendances externes et d'en restreindre l'accès.

Cette protection n'est pas une protection pour faire bien sur le papier et se gargariser. C'est une protection vis-à-vis des tiers, c'est même une méthode heuristique de gestion de la dette technique (©️ Julien Topçu).

Comment ?

Pour chaque dépendance externe, nous allons créer un dossier (et donc un espace de noms) avec à minima une interface et une implémentation de cette interface. Nous utiliserons nos interfaces dans nos injections de dépendances, l'autowiring de Symfony fera très bien son travail et notre code échangera avec nos méthodes au travers de contrats.

C'est vraiment la méthode de base à suivre et vous verrez que généralement, l'implémentation concrète est vraiment simple, voire ridicule, au point de déclencher des débats et claquer les plus belles poses de Yamcha aka “la grosse PLS” chez certaines personnes parce que tu comprends Alexandre, ça coute rien à changer, bim bam boum, rechercher/remplacer. Oui. Sauf que quand j'arrive sur des projets, le constat est simple personne, ou presque, ne le fait.

Alors je sors ma réponse simple et empirique, parce que comme on le sait tous et toutes, c'est toujours chez les autres et jamais chez nous :

Je traine depuis maintenant 9 ans certaines implémentations, je les ai fait évoluer rapidement, dans tous les projets, au travers de 5 versions majeures de Symfony. On est la pour coder. C'est un faux-débat.

Cas concrets

Quelques cas concrets pour illustrer le propos maintenant. Nous allons parler d'UUID, de Slug et Template. J'utilise pour ma part des architectures inspirées de DDD, Hexagonal et Clean Architecture, mais ce n'est pas un prérequis. Faites ce que vous voulez de vos espaces de noms, ce n'est pas ce point qui est le plus important alors n'en faites surtout pas un prérequis.

UUID

J'ai toujours utilisé le composant UUID de Ben Ramsey, mais Symfony a sorti son propre composant UID. J'étais plutôt dans la team “pourquoi ?” et puis j'ai rationalisé.

J'utilise tout le temps et par défaut des UUID v4.

<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Generator;

interface GeneratorInterface
{
    public static function generate(): string;
}

Le diff de l'implémentation (hors use) a donc été le suivant :

- return Uuid::uuid4()->toString();
+ return Uuid::v4()->toRfc4122();

Comme vous l'aurez compris, mon code utilise quant à lui toujours la méthode UuidGenerator::generate(). Le jour où je voudrais changer de point de vue sur la version de l'UUID, ça se passera ici et uniquement ici.

Slug

Créer un slug est un besoin récurrent, ne serait-ce que pour du SEO. Là aussi et pendant longtemps, j'ai utilisé la librairie cocur\slugify et Symfony a apporté un nouveau composant String.

L'interface est la suivante

<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Slugger;

interface SluggerInterface
{
    public static function slugify(string $string, string $separator = '-'): string;
}

Le diff :

- return (new Slugify())->slugify($string, $separator);
+ return (new AsciiSlugger())
+             ->slug($string, $separator)
+             ->toString();

Problème, contrairement à la librairie initialement utilisée, le composant ne touche pas à la casse du texte qu'on lui envoie. Il faut penser à lui dire de tout mettre en minuscule.

Comment est-ce que je l'ai vu ? Avec le test unitaire, très simple à écrire, qui va avec l'implémentation. Du coup je me suis fait avoir… Le temps de lancer le test pour vérifier le changement.

<?php

declare(strict_types=1);

namespace spec\App\Shared\Infrastructure\Slugger;

use App\Shared\Infrastructure\Slugger\Slugger;
use App\Shared\Infrastructure\Slugger\SluggerInterface;
use PhpSpec\ObjectBehavior;

class SluggerSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType(Slugger::class);
        $this->shouldBeAnInstanceOf(SluggerInterface::class);
    }

    function it_should_slugify_a_simple_sentence()
    {
        $this::slugify('A simple sentence')->shouldReturn('a-simple-sentence');
        $this::slugify('A Simple Sentence', '_')->shouldReturn('a_simple_sentence');
    }
}

J'avais oublié le lower, le texte retourné était A-simple-sentence/A_Simple_Sentence.

Le diff final :

return (new AsciiSlugger())
            ->slug($string, $separator)
+             ->lower()
            ->toString();

Je n'ai pas activé le support des emojis, nous verrons si cela arrive lors des projets et on verra ça au cas par cas.

Template

Dernier exemple pour la route, le fameux Twig. Oui, je fais du Symfony et encapsule du Twig dans un objet Template. Pourquoi ? Réponse dans la modification du changelog précédemment cité, mais, petite pensée pour Twig v1, v2 et enfin v3. Ca ne rajeunit pas ce genre de constats.

<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Templating;

interface TemplatingInterface
{
    /**
     * @param array<array-key, mixed> $parameters
     */
    public function render(string $name, array $parameters = []): string;
}

Et son implémentation (qui n'a pas bougé depuis un moment maintenant) :

<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Templating;

use Twig\Environment;

final class Twig implements TemplatingInterface
{
    public function __construct(private readonly Environment $twig)
    {
    }

    /**
     * @param array<array-key, mixed> $parameters
     */
    public function render(string $name, array $parameters = []): string
    {
        return $this->twig->render($name, $parameters);
    }
}

Oui je pourrais me payer le luxe de changer de moteur de template, d'en avoir plusieurs en parallèle et je ne le ferais probablement pas mais j'espère que vous avez compris que le point n'est pas la.

Pour terminer

Je fais la même chose pour d'autres composants et librairies tels que le Logger, le Mailer, des bus du composant Messenger, l'upload de fichiers et j'en passe. Comme vous le voyez, le cout technique est ridicule et il est en plus largement compensé lors que l'on veux changer notre façon de travailler avec du code tiers car le changement ne s'opère qu'à un seul endroit. Ce point d'architecture est donc dans ma case du “ça ne coute rien, ça couvre le cas où, c'est du quick win”.

Pour terminer, je pense également au composant Clock, composant officiellement introduit avec Symfony 6.2. Là encore, sourire au coin, un peu mesquin car je ne me souviens pas du nombre exact de discussions où l'on a pu m'expliquer que “la maitrise du temps” était une fois de plus un délire inutile associé au DDD. Ca ne m'empêchait pas d'encapsuler un DateTimeImmutable.

J'ai supprimé mon implémentation, l'ai remplacée par le composant Symfony, ai vérifié mes tests et… ça juste marche.

Cout technique de l'opération, 5 minutes.

J'ai eu à faire à beaucoup de projets Sylius ces derniers temps : développement, audit ou encore accompagnement d'équipe et architecture. À chaque prestation ou presque, une même question revient : quelle est la meilleure façon d'organiser ses templates ?

Au commencement : le SyliusThemeBundle

Ne vous laissez pas avoir par le nom, le SyliusThemeBundle est un bundle permettant d'ajouter une fonctionnalité de thème à un projet Symfony. Pour faire simple et compréhensible, un nouveau dossier “theme” est présent à la racine du projet et ajoute un niveau supplémentaire dans la hiérarchie de vos templates.

Étant donné qu'il s'agit d'un thème, vous pouvez en avoir plusieurs en parallèle. Le thème se déclare via un fichier composer.json très simple et vient surcharger le système de templates que vous connaissez déjà sur Symfony. Le thème se trouve au plus haut de la hiérarchie des templates de votre projet et dans la continuité du système de fallback : un fichier qui n'existe pas dans le dossier thème est un fichier qui sera recherché dans le dossier templates (puis dans un bundle etc).

Le thème à afficher peut se choisir de bien des manières et même très simplement. Tout est imaginable à ce niveau. Vous pouvez spécifier un thème par défaut pour votre projet et/ou déclencher l'activation d'un thème suivant vos critères : session, base de données, aléatoire (pour de l'A/B testing) ou encore suivant l'utilisateur connecté... Vous êtes libres d'implémenter vos propres règles et de les hiérarchiser.

Fonctionnalité intéressante, un thème peut en étendre un autre. Vous pouvez avoir un thème de base, puis d'autres qui ne feront que surcharger quelques fichiers : vive les usines à sites.

Qu'est-ce que l'on met dans un thème ?

Récapitulons : un thème n'est qu'un ensemble de fichiers Twig qui vont venir en surcouche des templates Symfony. On y met donc ce que l'on souhaite en respectant l'arborescence des templates du projet. Assets et translations peuvent également être ajoutés.

Passons maintenant au problème de fond, comment organiser tout ça.

Je vois, dans beaucoup de projets, des thèmes dans lesquels on met tous les templates sans se poser la question suivante : que se passe-t-il si le thème est désactivé ?

La réponse est assez simple, si vous désactivez un thème, vous ne voyez plus les éléments correspondants. C'est pourquoi il est très important de respecter la hiérarchie des templates et les fallbacks. Un thème surcharge la totalité de vos templates, interface d'administration comprise (dans le cas de Sylius), ce qui peut être un vrai problème si vous avez ajouté des champs de formulaires dans l'interface d'administration par exemple.

Et donc ?

L'arbre de décision est donc le suivant :

  • si un template doit TOUJOURS être présent dans un projet, que le thème soit activé ou non : il ne doit pas seulement exister dans le thème du projet, mais dans les templates (avant de potentiellement être surchargé dans un ou plusieurs thèmes),
  • si un template doit être visible UNIQUEMENT si le thème est activé : le template doit être dans le dossier themes correspondant,
  • si un template peut exister dans plusieurs thèmes différents, vous pouvez utiliser la hiérarchie des thèmes (parent/enfant) afin d'éviter les copier/coller.

Coté JS, il n'est pas possible détecter le thème pour ne faire un build que si le thème est actif. Ce n'est pas un problème : faites vos build en définissant la config webpack dans votre thème puis un require dans votre fichier webpack applicatif.

Une fois compilé, le coût technique est nul.

// webpack.config.js
const Encore = require('@symfony/webpack-encore');

// Votre config existante

// stuff
const front = Encore.getWebpackConfig();

// Fin de la config existante

Encore.reset();

const bootstrapTheme = require('./themes/BootstrapTheme/webpack.config');

module.exports = [front, bootstrapTheme];

Conclusion, respectez ces règles : vous serez joie et amour.

tl;dr : Dans cet article, qui change un peu du PHP, nous allons voir comment l'on pourrait adresser la problématique du vote électronique avec ce qu'on appelle vulgairement “la blockchain”.

Disclaimer

Cet article n'est pas une usine à lien vers des projets trop cool pour jouer à l'apprenti trader, c'est même assez compliqué puisque les sources sont souvent rattachées à des projets. Je ne citerais donc explicitement que Bitcoin et Ethereum.

Cet article n'est également pas une réponse absolue et dogmatique pour le vote électronique.

Le débat “vote électronique blockchain” est en cours comme à chaque élection, profitons-en pour discuter un peu entre personnes doux rêveurs technophiles.

Le contexte

En 2018 j'ai essayé de mettre en place un projet d'entreprise baptisé “Le Petit Bloc”. Cette société avait comme baseline, dans la langue de Shakespeare de “build a decentralised, trustable and reliable world”. Oui, votre serviteur avait de l'ambition.

L'objectif était de se servir des technologies de décentralisation pour adresser certains problèmes. On allait donc piocher dans le panel de technologies pour essayer, pour faire simple, de faire mieux.

Dans ce contexte, nous nous sommes nécessairement intéressés à tout ce qu'on met dans “la blockchain” et avons creusé un certain nombre de sujets. Parmi ces sujets, celui du vote électronique et tout le monde pourra convenir que du point de vue technique, c'est quand même un sujet intéressant à creuser.

État des lieux

Ce que l'on appelle vulgairement, de mon point de vue, “la blockchain” est un savant mélange de différentes technologies :

  • Un réseau pair à pair,
  • Une preuve,
  • De la cryptographie asymétrique,
  • Une base de données décentralisée, notre blockchain.

Chacun des éléments est indépendant et on retrouve ces éléments technologiques dans différentes situations. Ce n'est que l'assemblage de tout ces éléments qui donne un protocole tel que Bitcoin.

La chronologie des événements clés de ce que l'on connait actuellement et sur lequel on aime tant débattre, à savoir l'unité de compte bitcoin et le protocole Bitcoin, est à peu près la suivante :

  • 1971 : Richard Nixon met fin à la convertibilité du dollar. Un dollar n'est officiellement plus indexé sur son équivalent en or,
  • 1982 : David Chaum commence à réfléchir à un système de paiements intraçable avec le premier dollar électronique. Il est à mon sens l'élément déclencheur de la suite de la chronologie,
  • 1990 : David Chaum travaille sur l'e-cash et le système est testé dans certaines banques,
  • 1990 : Sholom Rosen travaille sur un système de monnaie électronique qui sera breveté,
  • 1992 : Tim May publie le Manifeste d'un crypto anarchiste issue du mouvement Cypherpunk dont l'un des pères spirituel est David Chaum,
  • 1993 : Eric Hugues publie le Manifeste d'un cypherpunk,
  • 1997 : Adam Back travail sur le HashCash au travers de la preuve de travail dans le but de lutter contre le spam,
  • 1998 : Wei Dai publie “b-money, an anonymous, distributed electronic cash system” qui servira de base au protocole Bitcoin,
  • 2000 : Freenet devient le premier réseau web distribué,
  • 2005 : Nick Szabo propose Bit-Gold, prémice de Bitcoin, qui n'obtient pas le soutien nécessaire. La résolution du problème des généraux byzantins est mise en avant,
  • 2008 : Satoshi Nakamoto publie “A peer to peer electronic cash system” qui servira de base au protocole Bitcoin,
  • 2018 : Alexandre s'intéresse à “la blockchain” et propose d'adresser le problème du vote électronique. (oh c'est bon, on rigole 😝)

Depuis 2008, des dizaines d'évolutions, de remises en question, mais toujours ces mêmes fondamentaux.

Qu'est-ce que c'est “un vote” ?

Ma compréhension du vote est la suivante : je dois parfois donner mon avis sur l'évolution de la société. Pour donner mon avis, on me propose de me déplacer dans un bureau de vote, de choisir une réponse dans un champ de possibilités et de donner ma réponse. Je signe alors une feuille permettant de déclarer mon vote en présentant ma carte d’électeur et une pièce d'identité. Je peux ne pas avoir ma carte d’électeur, mais je dois être inscrit sur la liste du bureau du vote. Les réponses sont comptabilisées par plusieurs personnes afin d'éviter la fraude et la somme de toutes ces actions permet d'acter une décision collective.

Il y a quatre étapes importantes :

  • Je suis un électeur et dois prouver mon droit de vote,
  • Je signe une feuille d’émargement permettant de ne voter qu'une fois pour un même scrutin,
  • Mon vote est anonyme,
  • Un système antifraude humain est présent.

Ce système de vote est à priori fiable dans un état qui fonctionne correctement, mais ne l'est pas nécessairement dans un pays qui n'est pas stable.

Une machine de vote électronique

Le vote électronique est déjà en place dans notre pays, en 2014 64 communes ont permis le vote électronique. Le vote électronique se fait au travers d'une machine, programmée par une société, des composants eux-mêmes programmable, possédant du code informatique, sans aucune possibilité de vérification pour les votants et c'est la somme de tous ces éléments qui amène à dire à bon nombre d'informaticiens que ce n'est certainement pas la bonne solution.

Dès lors, est-ce que l'on peut faire mieux ?


Du côté de la blockchain

Il n'existe aucun protocole de vote électronique basé sur une blockchain .

Par extension, les initiatives dApp ne remplissent pas nos critères, car elles sont pseudonymes et traçables.

Faisons tout de même le parallèle avec nos quatre étapes.

Comment est-on identifié ?

Pour échanger une unité de compte sur un protocole, vous devez posséder une adresse unique reliée à un portefeuille. Dans certains cas, une adresse peut être dérivée, un peu comme les différents espaces pour ranger vos cartes bancaires, pièces et billets.

L'adresse est numérique, mais le portefeuille peut être physique (un bout de papier) ou numérique, hardware ou software.

Comment les transactions sont-elles authentifiées ?

Une transaction correspond à une adresse, on n’accède pas à une adresse sans connaitre la clé privée permettant de la déverrouiller.

Comment gère t'on l'anonymat ?

Le protocole Bitcoin n'est pas anonyme, il est pseudonyme. Une adresse sur le réseau ne permet pas de savoir quel humain effectue une transaction, mais la totalité des échanges est vérifiable. Dès lors, lorsque l'on arrive dans un système faisant un lien entre adresse Bitcoin et une personne physique (via un KYC – Know your customer), on est capable d'identifier toutes les transactions. C'est d'ailleurs comme cela qu'on retrace certains piratages/rançons.

Il existe des protocoles anonymes, mais très peu le sont réellement. Différents exemples simple de faux anonymat :

  • une transaction dont on ne connait pas le montant, mais pour laquelle on connait l'émetteur et le récepteur est-elle anonyme ?
  • une transaction dont on ne connait ni l’émetteur ni le récepteur, mais dont le montant est connu via le registre est-elle anonyme ? (pour faire simple, si une adresse fait -153 et qu'une autre fait +153 c'est qu'on a probablement identifié l'échange)
  • une transaction anonyme sur un réseau comptant très peu de nœuds de vérification et donc facilement contrôlable est-elle réellement anonyme ?

Lors d'un échange entre deux adresses de manière anonyme, on fait intervenir un algorithme qui a pour but d'offusquer une partie ou la totalité de la transaction afin de la rendre intracable. On appelle ça un mixing. Cela sous-entend donc que l'on est technologiquement capable de rendre anonyme le contenu d'une transaction, sur un registre public. Si c'est un problème dans le cas d'un échange monétaire, ça ne l'est pas nécessairement dans le cas du vote.

Comment analyse-t-on les transactions ?

La blockchain est un registre distribué : un livre de comptes. Imaginez un livre dont chacune des pages correspond à un bloc. Vous êtes libres de mettre un nombre de transactions compris entre 0 et le nombre de lignes disponible sur chacune des pages. L'ordre des pages vous permet de remonter l'historique. Si vous n'êtes pas disponible, vous pouvez donner votre livre à quelqu'un d'autre. Si vous voulez vérifier qu'aucune bêtise n'a été faite, vous pouvez repartir de la première page, etc.

Dans la version la plus simple, on retrouve un système de blocs puis de transactions au travers d'un arbre de merkle et pour vulgariser, quitte à prendre des raccourcis, c'est la chaine ayant le plus de blocs valides qui l'emporte.

Il existe des alternatives par exemple au travers des graphs orientés acycliques ou encore des “sidechains”. Certains protocoles intègrent également des blocs “snapshots” permettant de rendre le calcul de l'intégrité de la chaine optionnelle.

On identifie également des niveaux “technologiques” autour de ces chaines. On parle alors de Layers. Bitcoin est une chaine de niveau 1 la ou Ethereum est une chaine de niveau 2, car possédant une machine virtuelle permettant d'effectuer des calculs. Il existe également le niveau 3 dont l'objectif est l’interopérabilité entre différentes chaines.

A noter que si Bitcoin est bien une chaine de niveau 1, il existe déjà des surcouches de niveau supérieur compatibles avec le protocole de niveau 1.

Comment fonctionne le système antifraude ?

L'un des arguments massue des défenseurs du vote électronique en version blockchain est la transparence. On ne peut que leur donner raison puisque le code de beaucoup de protocoles de monnaie électronique est ouvert et auditable. On peut également démarrer un nœud sur le réseau et vérifier l'intégralité des transactions afin de détecter une potentielle fraude.

Malheureusement, rien n'oblige à mettre en place cette transparence. C'est d'ailleurs pour cette raison que du point de vue technique, une blockchain privée n'est qu'une base de données décentralisée contrôlée, donc potentiellement modifiable, donc sans aucun intérêt.

Vient ensuite la preuve. Le protocole Bitcoin utilise la preuve de travail impliquant une dépense énergétique conséquente en échange de la validation d'un lot de transactions.

D'autres protocoles utilisent d'autres types de preuves. On retrouve par exemple la preuve d'enjeu (Proof Of Stake) qui consiste à mettre en gage un montant conséquent qui incite fortement à ne pas valider de fausses transactions au risque de perdre gros. Problème, ceux qui ont le plus de moyens contrôlent potentiellement le système.

Certaines preuves sont beaucoup plus folkloriques comme la preuve d'espace qui repose sur le coût de stockage et la capacité à acheter de l'espace disque. Je ne dis personnellement pas bravo.

Derrière une preuve, il y a une motivation à maintenir le réseau dans un état fonctionnel. Dans les systèmes actuels, cette motivation est purement financière.

Il existe des systèmes pour contrer des attaques ayant pour but de changer le registre, des systèmes pour contrer des “super calculateurs” mais le système anti-fraude repose également sur un consensus. Il peut donc y avoir des divergences d'opinions et donc des “forks”.


Mon vote électronique

C'est le moment de l'article où je dis que je n'ai malheureusement pas toutes les solutions. Et oui.

Comment est-on identifié ?

Créer une adresse numérique, pouvoir la récupérer en cas de perte c'est quelque chose de très bien, mais ça ne permet pas de gérer les personnes qui n'ont pas accès ou ne peuvent pas accéder à un ordinateur.

On pourrait imprimer des adresses, les papers wallets existent et il suffirait alors de scanner le code pour authentifier la personne, mais de la même façon qu'une carte d’électeur est doublée d'une carte d'identité, il faudrait que le paper wallet soit adossé à un autre document.

Quelle que soit l'option choisie, on arrive dans un problème inhérent à notre système que je résume sous la thématique suivante : la gestion de l'identité (numérique).

Ce problème doit d'abord être adressé. Tant qu'il ne le sera pas, on ne tient à mon sens, pas une solution pour du vote électronique et une double authentification (avec un email/sms par exemple) ne me parait personnellement pas suffisante. On le constate déjà sur des applications web.

Alors oui, nous pourrions continuer de demander aux électeurs de se déplacer mais l'un des éléments du débat en faveur du vote electronique est justement la lutte contre l'abstentionnisme.

Si on devait me donner carte blanche à ce niveau, j'irais personnellement transposer le protocole Macaroon sur des technologies acycliques pour aller adresser d'autres problèmes au passage, mais ce n'est pas le sujet.

Comment les transactions sont-elles authentifiées ?

Lorsque l'on vote, on signe une feuille. Je n'ai pas envie de changer cette règle. Pour cette raison, je pense que l'on doit utiliser non pas une chaine, mais deux chaines (via les sidechains ou les layers) et cette première chaine doit être pseudonyme afin d'être vérifiable.

Cette première chaine ne doit stocker que deux informations, la personne qui vote et le scrutin correspondant. Cette chaine est alors une transposition exacte de notre carte d’électeur et du coup de tampon associé ainsi que de la signature sur le registre. Si on regarde une carte d’électeur ou que l'on analyse tous les registres de votes, on connait au minimum le nombre de fois ou l'on a voté. Est-ce que c'est un problème ? Probablement pas et si ça devenait un problème, il suffirait alors de créer des chaines différentes pour chaque scrutin.

Cette intention de vote permet de valider le contenu du vote. Cela sous-entend que dans le cas où un vote frauduleux passerait dans le système, on pourrait l'invalider a posteriori et le décompter. De la même façon, si quelqu'un venait à voter pour quelqu'un d'autre, on pourrait tracer cette information. Les personnes qui ne veulent pas voter pourraient s'assurer qu'aucun vote contenant leur adresse n'est présent d'une manière beaucoup plus simple que d'arriver en bureau de vote et d'exiger de voir si l'on est présent ou non sur le carnet de signatures.

Tout ça n'est finalement qu'une transposition de ce que l'on fait déjà au travers de certains protocoles dits anonymes.

Comment analyse-t-on les transactions ?

Ça se complique un peu plus. Comme je l'ai dit, nous aurions deux chaines. La première chaine correspond à l'intention de votes et la seconde chaine contient le vote par lui-même.

Il ne doit pas être possible aux nœuds de validation du contenu du vote de remonter vers les nœuds de validation de l'intention de vote et le protocole ne doit que donner un go/no go pour accéder de la première à la seconde chaine. On a donc bien deux protocoles différents.

Concernant le décompte, nous pouvons nous tourner vers un protocole complexe basé sur un échange de clés. Au moment du vote, le contenu serait chiffré et ne pourrait être vérifié que par la clé du candidat correspondant. Chaque vote serait analysé tour à tour et le compteur mis à jour en conséquence. Ces clés pourraient être stockées à différents endroits : les partis politiques pourraient ne compter que leurs propres voix là ou d'autres comme le gouvernement pourraient compter la totalité des voix. On pourrait faire la somme des comptes pour chacun des partis pour ensuite la comparer au nombre de votants (en incluant un noeud pour le vote blanc).

Il y a donc plusieurs solutions. Reste à les implémenter et pourquoi pas, les voter.

On continue donc d'utiliser la base de nos protocoles dits anonymes et on rajoute quelque chose qui ressemble fortement à un échange PGP : le Verifiable Secret Sharing, composant indispensable des systèmes E2E.

Comment fonctionne le système antifraude ?

Nous l'avons vu dans les postulats du vote anonyme, il nous faut un nombre de nœuds importants afin de pouvoir détecter une fraude locale. Il nous faut également un code source accessible et auditable.

Quid de la nature de la chaine ? Publique, privée ou sous consortium ? Une chaine privée n'a aucun sens, une chaine publique est un idéal, une chaine sous consortium pourrait être justifiable sous condition d'un code source accessible et auditable, mais surtout d'un compte rendu de l'état de chacun des noeuds ainsi que du registre par des tiers.

À mon sens, les différents organes du gouvernement doivent bien entendu posséder des noeuds de validation, mais aussi les différents partis politiques et la presse. On pourrait même imaginer des nœuds de validation au-dessus du pays afin de valider un résultat définitif. Le système antifraude commencerait alors à se construire naturellement, car si quelqu'un triche, il pourrait être détecté. Dans le où différents acteurs se mettraient d'accord pour tricher, on pourrait appeler les noeuds supérieurs qui pourraient alors déclarer une fraude massive et invalider une élection. Une fraude massive décrédibiliserait la totalité du scrutin, des participants et pourrait avoir des répercutions sur les notes de fiabilités des pays et donc leur capacité d'endettement sans oublier l'indice même de confiance des votants réduisant l'envie de jouer avec le feu.

Malheureusement, cela fait beaucoup de “pourrait” et ne règle pas de manière technologique le problème des généraux byzantins. Quel est le couperet qui empêcherait la fraude ? Introduire une motivation monétaire dans un état qui maîtrise ses flux financiers ? Impossible. Quelque chose de “plus simple” comme l'annulation de la candidature d'un élu fraudeur ? Il faudrait certainement modifier les lois. Je pense que nous sommes tout simplement coincés ou que nous devons accepter que la résolution du problème fraude doit passer par l'humain et donc envisager que cela ne puisse pas aboutir.

La motivation ne peut qu'être la satisfaction d'un état fonctionnel et ça, c'est à priori une notion très personnelle. Tant que tout va bien, pourquoi pas, mais dès que l'on se trouve dans un état qui ne va pas bien politiquement ? Après tout, on trouve tout un lot de mauvaises personnes qui sont très satisfaites d'un état qui ne fait que servir leurs intérêts et la définition d'un état fonctionnel dans un même pays n'est pas la même suivant les personnes.


Conclusion

Comme vous pouvez le voir, ma vision technique du vote électronique ne repose absolument pas sur quelque chose de révolutionnaire. De la même façon que “la blockchain” n'est qu'un assemblage de différentes technologies, je ne propose que d'aller chercher des technologies déjà existantes et d'essayer de les mettre en oeuvre côte à côte pour apporter une possible réponse à la question du vote électronique.

Malgré tout, mon analyse personnelle m'amène à penser que cela n'est pas possible tant que l'on n'arrive pas à authentifier numériquement chaque votant et à garantir à tous la possibilité de voter.

Enfin, la mise en oeuvre. Dans le cas où une personne serait suffisamment brillante pour implémenter ce que je propose et adresser tous les problèmes que je n'ai pas encore imaginés, cette solution de vote électronique impliquerait une remise en question du système dans son ensemble et en est-il capable ?

Pour ces raisons je conclurais donc : ne faisons pas de vote électronique et commençons par faire en sorte de rétablir le dialogue et changer les modes de scrutins afin de ne plus voter contre mais pour. Travaillons pour la confiance dans le système, car en l'état, vote électronique ou pas, technologies ou pas, blockchain ou pas, rien ne va.

Bref,

“Tout cramer pour repartir sur des bases saines” ©Leodagan 2022.

Si vous ne l'avez pas encore remarqué, je commence beaucoup de choses, je disparais, je reviens avec des todos monstrueuses et ainsi de suite.

Pour faire simple, l'année 2020 n'a définitivement pas été facile et m'a obligé à mettre de côté un nombre incroyable de sujets. Le crunch est entrain de passer, l'année 2020 pour Vanoix est largement sauvée, est probablement la meilleure depuis la création de la société malgré un premier semestre désastreux et, même si différente, l'année 2021 promet son lot de surprises à tous les niveaux.

En vrac pour le perso (et pas forcément dans l'ordre)

  • Je fais maintenant partie du bureau de l'AFUP en tant que Vice-Président de l'association. J'avais pris la décision en 2020, Cécile a bien voulu de moi pour l'aider et je suis personnellement très content de pouvoir enfin me consacrer à l'AFUP ;
  • J'ai eu le plaisir d'être invité dans Message à caractère informatique pour discuter de différents sujets. Joie particulière de retrouver Quentin, ça fait bien trop longtemps que l'on n'avait pas discuté ;
  • J'ai une chaine Twitch, c'est encore très rudimentaire, mais personnellement, le format me plait. Cela va me permettre d'alimenter le côté OSS.

En vrac pour l'OSS (et pas forcément dans l'ordre)

Énormément de Sylius depuis le mois de juillet. J'accompagne une agence qui souhaite pivoter tout son ecommerce sous Sylius dans le cadre d'une prestation Vanoix. Nous avons écrit 26 plugins dont 7 sont censés revenir à l'OSS après lancement du projet (via le contrat OSS mis en place par Vanoix).

J'ai pris un peu de temps pour travailler sur les sujets suivants :

  • ModernPluginSkeleton qui me permet de bootstraper des plugins Sylius selon un nouveau format (compatible Symfony 4.x) ;
  • SyliusPluginStandard qui est une version modifiée de Sylius afin de me permettre de développer plus facilement les plugins dans un environnement “brut” ;
  • TemplateGenerator qui ne sert pour le moment pas à grand chose, mais qui devrait me permettre à terme de mieux bootstraper mes plugins/bundles.

J'ai également des choses qui devraient arriver coté Symfony mais chuuuuut.

Conclusion

La todo était énorme, elle l'est encore plus. Sylius est maintenant disponible en version 1.9 (sur une base de Symfony 5.2) ce qui m'oblige à faire monter tout l'existant mais, bien pour un mal, me permet “d'oublier” PHP 7.2 et Symfony 4.4 au quotidien.

J'ai vraiment hâte de passer aux étapes suivantes que j'espère monstrueuses en termes d'avancées pour la société, les ami·e·s, l'OSS et donc moi.

Merci à toutes les personnes qui ont été présentes en 2020, elles se reconnaîtront ❤️.

Petite astuce, car non documentée officiellement, concernant Sylius. Si comme moi vous créez/utilisez des plugins, vous avez dû vous apercevoir que les nouveaux éléments de menus apparaissaient toujours en dernier.

Ce comportement est normal car il s'agit de listeners qui sont appelés les uns à la suite des autres en suivant l'ordre de déclaration. L'astuce consiste donc à créer un listener qui sera toujours appelé en dernier et qui ne servira qu'à modifier l'ordre des items.

Dans un premier temps, on va créer le listener.

<?php

declare(strict_types=1);

namespace App\Menu\Event;

use Sylius\Bundle\UiBundle\Menu\Event\MenuBuilderEvent;

final class AdminMenuListener
{
    public function reorderMenu(MenuBuilderEvent $event): void
    {
        $menu = $event->getMenu();
        $menu->reorderChildren([
            'catalog',
            'foo_menu', // plugin foo
            'bar_menu', // plugin bar
            'sales',
            'customers',
            'marketing',
            'configuration',
        ]);
    }
}

Puis on déclare la configuration de ce listener en pensant à lui donner l'ordre de priorité le plus bas (tous les éléments du menu doivent être connus avant d'être réordonnés).

services:
    App\Menu\Event\AdminMenuListener:
        tags:
            - { name: kernel.event_listener, event: sylius.menu.admin.main, method: reorderMenu, priority: -256 }

And voilà !

Attention :

  • Cet article fait partie d’une série consacrée à de grands principes d’architecture qui me sont propres et a pour objectif de faire comprendre aux tiers comment évoluer sur mes propres projets.
  • Cet article évolue suivant les retours et les différentes expériences.
  • Le terme Domain n'implique pas l'obligation d'une conception orientée DDD.

Le design pattern ADR est basé sur le triptyque ActionDomainResponder. Il est l’une des alternatives au design pattern MVC.

Il est possible de vulgariser le pattern de la façon suivante :

  • Action : l’équivalent d’un contrôleur, dédié à une opération HTTP (une route) de l’application ;
  • Domain : le point d'entrée logique vers le code métier ;
  • Responder : en charge du traitement et du rendu de la réponse.

Pourquoi ?

Le pattern MVC invite à créer des controllers composés d'une ou plusieurs actions. A l'usage, ces controllers peuvent devenir conséquents voir problématiques car trop complexes. L'ADR permet de limiter cette complexité globale. Il sera toujours possible d'avoir du code complexe, mais cette complexité sera limitée à une action dans un contexte précis.

Étant donné que l'ADR est composé d'un triptyque, le pattern encourage naturellement la POO et un SRP simple dans le sens ou les traitements métiers seront plus naturellement délégués à des “services”.

Enfin, l'ADR via son Responder assure une cohérence dans le rendu des réponses de l'application.

Comment ?

Si une Action est sensiblement la même chose qu’un controller à savoir être en charge du traitement de la requête entrante, le Responder peut quant à lui être conçu de manière différente.

Le moyen le plus simple consiste à mutualiser les Responder en les dédiant à un type de réponse. Dans une application HTTP classique, on pourra ainsi créer différents types de Responder :

  • HtmlResponder ;
  • RedirectResponder ;
  • DownloadResponder ;
  • JsonResponder ;
  • XmlResponder ;
  • ...

Plus complexe, mais conforme à la définition initiale du pattern, le ou la développeur·euse dédiera une action donnée à un Responder unique. On utilisera alors cette spécialisation pour les cas où le formatage de la réponse est complexe.

Afin de conserver la qualité de notre application, ce Responder pourra encapsuler un Responder commun (via la composition).

Enfin, l'utilisation la plus complexe consistera quant à elle à ajouter un Payload afin de spécialiser la réponse ainsi que son contenu.

Dans une application orientée DDD, l'Action et le Responder seront situés dans la couche User Interface. L'emplacement de la couche Domain dépend quant à elle des choix d'architecture de l'application. N'y voyez, une fois de plus, qu'un terme commun à d'autres approches de conception.

Exemples

<?php

namespace App\UI\Front\Action;

use App\UI\Front\Responder\HelloResponder;
use App\Application\Service;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

final class HelloAction
{
    private Service $service;

    private HelloResponder $responder;
    
    public function __construct(
        Service $service, 
        HelloResponder $responder
    ) {
        $this->service = $service;
        $this->responder = $responder;
    }
    
    public function __invoke(ServerRequestInterface $request): ResponseInterface
    {
        // 1. entrée
        $data = (string) $request->getBody();
        
        // 2. traitement (Domain)
        $service = ($this->service)($data);
        
        // 3. sortie (Responder)
        return ($this->responder)($service);
   }
}
<?php

namespace App\UI\Front\Responder;

use App\Application\Service;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\HtmlResponse;

final class HelloResponder
{   
    public function __invoke(Service $service): ResponseInterface
    {       
        $response = $service->getData();
        
        return new HtmlResponse($response);
   }
}

Symfony

L'utilisation de l'ADR n'est pas mise en avant dans Symfony. Il faudra donc veiller à sensibiliser l'équipe à ce sujet. A contrario, l'autowiring de Symfony étant performant, il est possible d'utiliser l'ADR sans aucune modification particulière du framework. Il n'y a donc aucune contrainte technique.

De par sa conception, on évitera d'injecter les différents services utilisés par une Action dans la méthode invoke et on privilégiera l'injection via le constructeur.

Si l'utilisation de Twig n'a pour le moment jamais été remise en question par les auteurs de Symfony, des changements dans les best practices ont eu lieu à plusieurs reprises sur les controllers. L'utilisation de l'ADR a pu permettre de limiter l'impact lors des migrations. Il est concrètement possible de les implémenter depuis la version 2.7 de Symfony.

Questions/Réponses

Aucune TwitterEmail

Conclusion

L'ADR est une alternative très intéressante au MVC et adaptée au flux Request/Response. Le coût technique de son utilisation n'est pas important, mais son impact est fort. C'est également un cadre plus strict avec un fort pouvoir d'adaptation.

Certain·e·s lui reprocheront sont coût cognitif avec comme argument principal l'impact sur la directory structure du projet (il existe un fichier par action), mais il est alors facile de répondre que ce coût est largement compensé lorsque l'on visualise le code.

Pour terminer, cette segmentation permet également de faciliter les tests unitaires.

Attention :

  • Cet article fait partie d’une série consacrée à de grands principes d’architecture qui me sont propres et a pour objectif de faire comprendre aux tiers comment évoluer sur mes propres projets.

  • Cet article évolue suivant les retours et les différentes expériences.

  • User Interface fait partie des termes utilisés en DDD. N'y voyez cependant qu'un terme commun et une inspiration (forte mais une inspiration tout de même).

Le terme Interface Utilisateur (User Interface ou encore UI) désigne les différents moyens d’une application de communiquer avec un système extérieur : humain ou machine.

On trouve différents ensembles d’Interfaces Utilisateur :

  • HTML
  • API
  • Console
  • Message Queue
  • ...

Tous ces ensembles peuvent eux-mêmes être divisés. Une Interface Utilisateur HTML pouvant par exemple représenter la partie vitrine d’un site internet et une autre Interface Utilisateur l’administration. Autre exemple, une Interface Utilisateur API peut quant à elle exister au travers de différentes versions clairement identifiées.

Pourquoi ?

Dans un système complexe et destiné à évoluer, il est plus simple de segmenter ses différentes Interfaces Utilisateur et de les identifier clairement.

Cette segmentation est aussi bien visuelle : les éléments d’une Interface Utilisateur sont regroupés dans un même package, que technique : il est plus facile de leur donner des prérequis différents, de les faire évoluer dans le temps ou encore de les déprécier.

Une Interface Utilisateur est également décorrélée des autres couches de l'application (qui sont quant à elles chargées du traitement métier). Cela sous-entend que différentes Interfaces Utilisateur effectuant le même traitement encourageront naturellement le ou la développeur·se à utiliser des services identiques et les bonnes pratiques SOLID.

Les Interfaces Utilisateur peuvent également fonctionner de manière autonome avec des données de test ou temporaires et simuler un traitement sans impliquer d’autres services (via de la donnée brute par exemple).

Supprimer une Interface Utilisateur ne devrait pas avoir d'impact sur les autres Interfaces Utilisateur mais seulement sur les systèmes extérieurs en lien avec l'Interface Utilisateur (exemple : moteur de recherche, visiteur...).

Exemples

<?php

namespace App\UI\Front\Action;

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\HtmlResponse;

final class Hello
{
    private Service $service;
    
    public function __construct(Service $service)
    {
        $this->service = $service;
    }
    
    public function __invoke(ServerRequestInterface $request): ResponseInterface
    {
        // 1. entrée
        $data = (string) $request->getBody();
        
        // 2. traitement
        $response = $this->service->__invoke($data);
        
        // 3. sortie
        return new HtmlResponse($response);
   }
}
<?php

namespace App\UI\Console\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

final class HelloCommand extends Command
{
    protected static $defaultName = 'app:hello';

    private Service $service;
    
    public function __construct(Service $service)
    {
        $this->service = $service;

        parent::__construct();
    }
    
    protected function configure()
    {
        // arguments
    }
    
    protected function execute(InputInterface $input, OutputInterface $output): ResponseInterface
    {
        // 1. entrée
        $data = $input->getArgument('foo');
        
        // 2. traitement
        $response = $this->service->__invoke($data);
        
        // 3. sortie
        $ouput->writeln($response);

        return Command::SUCCESS;
   }
}

Éléments annexes

Tous les éléments en lien avec l’extérieur font partie de l'Interface Utilisateur.

Cela implique par exemple les formulaires. On créera ses formulaires (et les éléments relatifs à son traitement) dans un namespace tel que App\UI\{...}\Form\{DTO,Type,Handler,...}.

A propos des formulaires, ces derniers font partie de l'Interface Utilisateur car ils sont intimement liées aux données entrantes, données qui seront ensuite traitées par les autres couches de l'applications. Cela introduit un potentiel lien avec un composant tiers (comme le composant Form de Symfony) mais ce lien, dans ce cas précis, n'est actuellement pas considéré comme problématique car la valeur ajoutée globale de l'Interface Utilisateur est plus importante.

Dans le cas de l'intégration dans un système tiers (plugin, extension, surcouche...), il est conseillé de considérer l'outil principal comme une Interface Utilisateur. Cette considération permettra alors de segmenter clairement la partie du code en relation avec l'outil et le code métier ajouté. Une fois encore, le changement d'outil et donc d'Interface Utilisateur ne doit pas impacter le code métier.

Symfony

Certaines règles sont en désaccord avec les bonnes pratiques officielles Symfony concernant les formulaires :

  • L’Interface Utilisateur encourage l’utilisation des Data Transfer Object et un traitement en deux temps (les traitements backend devraient être agnostiques des Interfaces Utilisateur) ;
  • Dans le cas d’un même formulaire évoluant avec les mêmes données sources dans deux Interfaces Utilisateur, on encouragera la duplication du Type ;
  • Sur la base du point précédent, compte tenu de la faible probabilité d’avoir deux Interfaces Utilisateur remplissant le même rôle avec les mêmes données sources, on déconseillera l’utilisation des Extensions sur autre chose que des types primitifs fournis par le framework et/ou des bundles. Si de telles extensions doivent être créées, elles ne doivent pas faire partie du package UI mais de l'Infrastructure.

Questions/Réponses

Aucune TwitterEmail

Conclusion

L’Interface Utilisateur est l’une des couches composant une application. Il est important de la traiter aussi bien que le reste de l’application, car c’est celle qui, dans le temps, pourra être constituée de code spaghetti. Charge à vous, sur cette base, de trouver votre meilleure façon de faire, qu’il s’agisse d’un modèle de type MVC, ADR ou maison.

TL;DR : une note (très personnelle) concernant mon parcours de conférencier avec un petit teaser concernant l'avenir.

Au commencement

J'ai commencé à donner des conférences avec l'AFUP (pour l’antenne de Lyon) en 2014. Je faisais 20kg de moins, j'étais rasé de près et j'évoquais alors l’utilisation de la sémantique dans le but de faciliter la conception du modèle d’un projet. J'ai ensuite rapidement enchaîné sur le sujet de l’intérêt d’ouvrir ses process au travers d’une conférence sur l’entreprise Open Source.

Ma première grosse conférence au Forum PHP parlait quant à elle de l’organisation de projets DDD. J’ai organisé, à peu près au même moment, le DDD-day à Lyon. Il y a eu ensuite différents REX [1] [2] sur des antennes locales et au travers de différents Meetups/BBL pour terminer sur la récente conférence de l’AFUP Day consacré à la programmation défensive.

Entre temps j’ai parlé d’autres sujets, mais toujours très peu de technique. Pour rester factuel, environ 90 % de mes conférences techniques ont eu lieu sur Bitcoin et sur la création de Smart Contract Ethereum. Le tout avec différents niveaux de technicité suivant le public présent. Je n’ai jamais réussi à parler de tout ça dans l’écosystème PHP (sauf chez M6Web), c'est officiellement mon seul regret jusqu'à maintenant.

L'avenir

Je ne proposerai plus de conférences sur mes thématiques de prédilections. Je pense avoir dit ce que j’avais à dire, plus ou moins bien, et tout ça sera encore valable pendant quelques années.

Si tout se passe bien, l'avenir sera consacré à des applications réelles, des choses concrètes, des choses que l’ont peut voir et toucher du doigt (vous l’avez ?) et d’autres idéaux.

Je sais que c'est encore un peu (trop) vague. Certains sujets mettent du temps à être posés et nécessitent énormément de travail en amont. J'ai commencé en début d'année, confinement, nécessité de se mettre à fond pour rattraper le temps. Ça arrivera forcément à un moment.

En attendant

J’avais annoncé en début d’année que je me tenais à disposition des antennes AFUP locales et de tous les autres. C'est toujours le cas et j’aurais bien entendu immensément de plaisir à retravailler ces sujets que je ne quitte pas professionnellement (bien au contraire) puis à vous les présenter. N'hésitez pas à venir en discuter.

Pour conclure, j'espère sincèrement que l'on pourra tous se voir au Forum PHP et que coco le virus ne viendra pas nous embêter. A bientôt ❤️.

J'ai laissé un peu d'eau couler sous les ponts et ai finalement décidé de partir sur deux possibilités :

  • Utiliser “main” pour la majorité de mes projets. Un consensus a émergé très rapidement autour du terme qui est suffisamment explicite à mon gout ;
  • Utiliser l'option proposée par Fabien Potencier pour les projets plus conséquents.

Deux précisions tout de même.

Je pense avoir donné mon point de vue au travers de différentes discussions. Cela fait très longtemps que je pense que la société ne va pas dans le bon sens. J'en discute régulièrement et énormément avec certaines personnes. Je suis comme beaucoup très embêté que cela aille jusqu'à la remise en question de termes techniques, MAIS le fait est que cela me semble nécessaire à bien des égards et que toutes les discussions autour de ce sujet ont été utiles pour certain.e.s. Si changer le nom de mes branches me permet d'avoir encore de nouvelles discussions autour du sujet et que ces discussions permettent à un peu plus de personnes de prendre conscience des choses, ça me convient.

À l'opposé, penser que l'on règle le problème en renommant sa branche principale sur GIT est aussi stupide que d'illustrer ce changement avec des extrêmes qui n'ont rien à voir. Le monde du web est trop peu politisé et je pense que cela n'est pas une bonne chose. Nous avons la capacité de faire beaucoup plus grâce à nos lignes de code et le temps est venu d'endosser ce rôle.

J'espère sincèrement que vous prendrez le temps d’élever le débat dans votre for intérieur et que vous vous intéresserez aux “vrais” problèmes, ceux qui sont liés à ces discussions. Ils existent, ils ont besoin d'être adressés et nous sommes en capacité d'essayer de les résoudre.

TL;DR : J'ai profité du temps de confinement pour continuer à monter en compétence sur Sylius. Différents plugins ont été créés pour couvrir des besoins dans une boutique que je gère mensuellement. Le premier à être release est le plus simple : un plugin Cookie Alert.

Sylius est une solution ecommerce basée sur le framework PHP Symfony. Cette solution se veut simple, efficace et n'a pour objectif que de couvrir le besoin de la vente en ligne. Encore jeune, Sylius ne possède pas tout à fait un écosystème de plugin aussi étoffé que ses concurrents (Prestashop/Magento en tête), mais si tout avance comme prévu dans la roadmap, cet écart est destiné à se combler. Chose intéressante, beaucoup d'ecommercants et de sociétés spécialisées sur Magento se tournent vers Sylius.

Étant un habitué de Sylius depuis longtemps, j'ai pu goûter aux joies des migrations à de multiples reprises et ai toujours réagi suivant les besoins et demande des projets. L'un des projets a récemment été mis à jour et ayant un peu de temps, confinement oblige, j'ai pris le parti de pleinement embrasser la version 1.7 et ses modifications.

Nouvelle organisation des entités

Les précédentes versions de Sylius vous invitaient à étendre les entités à la demande vous obligeant à créer des fichiers, de la configuration et au moindre oubli, vous sanctionner avec des erreurs Doctrine.

Sylius 1.7 change un peu les choses, les entités sont déjà étendues et la configuration par défaut est écrite. Ça ne parait rien comme ça, mais coté DX, c'est quand même beaucoup plus simple. Changement adopté.

Semantic UI + Gulp vs Bootstrap + Encore

Pour faire simple, je n'aime vraiment pas Semantic UI et Gulp. Je ne vois pas quoi ajouter. N'étant pas un grand fan de JavaScript et ayant toujours eu mieux à faire que de m'intéresser réellement à tout ça (hormis une vraie tentative avec React), j'ai vraiment regretté le choix de Semantic UI lorsque je souhaitais ajouter des dépendances JavaScript car tout ce que j'avais l'habitude de faire avec Bootstrap était à reprendre.

Sylius 1.7 ouvre officiellement la piste de Bootstrap et Encore. Petit couac sur le theme Bootstrap en version 1.7, il n'est pas encore entièrement compatible (spoil : je me suis mis ce point sur ma todolist). C'est donc avec beaucoup de joie que j'ai greffé Bootstrap, migré tout mon JavaScript sur Encore et... Bonheur.

Themes

Je n'utilisais pas les thèmes, j'ai révisé ma position au passage en créant un thème enfant à celui de bootstrap. Aucun problème particulier, je vous conseille de vous y mettre.

Événements de thèmes

Sylius a revu sa copie à ce niveau en passant de SonataBlock à son propre système de block, quasiment identique, ayant pour but de pouvoir manipuler son interface utilisateur de manière plus simple. J'en ai beaucoup discuté avec la personne en charge de la boutique, on a parlé des avantages et des inconvénients, comparé avec la concurrence (notamment Prestashop) et nous en sommes venu à la conclusion que sur le long terme, il y avait un réel intérêt à se mettre au plus tôt à l'utilisation des événements.

Plugins

Comme beaucoup, j'écris mon code dans src/ et l'organise en conséquence. J'ai décidé de me mettre sérieusement aux plugins car je pense que l’écosystème en a réellement besoin et qu'il y a vraiment des choses à faire, certes plus ou moins intéressantes, mais il y a à faire.

Plusieurs plugins ont donc été créés dont ce premier plugin, très bête, dont nous allons parler.

SyliusCookieAlertPlugin

CookieAlert a pour objectif simple d'afficher un bandeau en bas de page pour informer de l'utilisation de cookies. Ni plus ni moins. Ce plugin utilise une dépendance JavaScript et les événements pour s'ajouter au bon endroit avec un simple template. Et c'est tout (pour le moment).

Il m'a par contre permis de remonter différents points :

  1. La stack de test des plugins est une plaie pour moi qui ai quasiment tout dockerisé sur mon ordinateur. Ce plugin qui modifie uniquement l'UI du front n'est donc pas testé et je ne le considère pas stable le temps de régler ce problème d'une manière ou d'une autre.

  2. Je ne sais pas encore quel serait le meilleur moyen pour prémâcher le travail d'intégration des dépendances JavaScript et si créer deux fichiers SASS/JS, destinés à être consommés par Encore est réellement une bonne idée.

Installation

Rien de très compliqué, il y a aura une PR pour rendre le plugin compatible avec flex dès qu'il sera stable.

  • Ajouter le plugin dans son composer.json

composer require black/sylius-cookie-alert-plugin:^1.0.0@dev

  • Activer le plugin :
<?php

// config/bundles.php

return [
    // ...
    Black\SyliusCookieAlertPlugin\BlackSyliusCookieAlertPlugin::class => ['all' => true],
];
  • Importer la configuration
# config/packages/sylius_cookie_alert.yaml
imports:
    - { resource: "@BlackSyliusCookieAlertPlugin/Resources/config/app/config.yml" }
  • Installer la dépendance JavaScript yarn add bootstrap-cookie-alert
  • Ajouter la dépendance dans son fichier principal :
  require('bootstrap-cookie-alert/cookiealert');
  • Ajouter le css du plugin (j'utilise SASS)
@import '~bootstrap-cookie-alert/cookiealert.css';

Rendu final

Le résultat

Sponsoring & contributions

Ce plugin est officiellement sponsorisé par Vanoix qui m'a rémunéré pour la création de ce plugin mais aussi de tous les autres. Nous aurons l'occasion d'en reparler très bientôt.

Vos bonnes idées sont bien entendu les bienvenues. Ce plugin ne parait rien comme ça, mais il reste un indispensable et pourrait servir de base à quelque chose de beaucoup plus conséquent si l'on prend en compte tout ce qu'il y a à faire autour du RGPD.