Le plugin YAML v2

Depuis sa création, le plugin YAML a subi peu de mises à jour. Sa stabilité est une preuve de robustesse, néanmoins le petit toilettage et les quelques évolutions de la branche v2 apportent plus de souplesse dans l’utilisation et simplifieront la maintenance.

Versions et compatibilité

Cette branche v2 du plugin est dédiée à SPIP 3. La compatibilité avec SPIP 2 a été abandonnée (suppression du plugin.xml) et en conséquence le PHP 4 n’est plus supporté. La borne inférieure de compatibilité SPIP a été fixée à 3.0.0, néanmoins, il est nécessaire de posséder une version PHP supérieure ou égale à 5.3.3 pour être assuré du fonctionnement correct du plugin.

Par défaut, sans rien modifier de la configuration, le plugin utilise la même librairie YAML que la branche v1, si ce n’est que celle-ci a été rafraichie comme précisé dans un chapitre ci-après.

Même si le prototype des fonctions d’API a changé (voir le chapitre suivant), le code d’appel des fonctions v1 est toujours valable et donnera les mêmes résultats. Néanmoins, il est conseillé de migrer progressivement vers une utilisation type v2 (voir le chapitre concerné).

L’API du plugin

L’API du plugin YAML (fichier inc/yaml.php) est composée de 3 fonctions principales déjà présentes en v1 et d’une fonction dépréciée conservée par souci de compatibilité avec la branche v1. Les 3 fonctions principales sont donc :

  • yaml_encode($structure, $options = array()), qui encode une structure de données PHP en une chaîne YAML ;
  • yaml_decode($input, $options = array()), qui décode une chaine YAML en une structure de données PHP, souvent un tableau ;
  • yaml_decode_file($file, $options = array()), qui décode un fichier YAML en une structure de données PHP. Cette fonction fait appel à yaml_decode() après avoir extrait le contenu du fichier.

Par rapport à l’API v1, le prototype des fonctions a été modifié avec l’ajout systématique de l’argument optionnel $options qui agrège l’ensemble des options possibles dans un tableau associatif. Son utilisation exhaustive est précisée dans le chapitre décrivant l’utilisation avancée du plugin.

Ces fonctions sont indépendantes de la librairie YAML utilisée. En fonction du contexte - constante _LIB_YAML et index ’library’ de l’argument $options - chaque fonction de nom $fonction détermine la librairie à utiliser $library et fait appel à la fonction « homonyme », $library_$fonction().

La fonction yaml_charger_inclusions() peut être considérée comme dépréciée dans cette nouvelle branche. Même si il est toujours possible de l’utiliser pour intégrer récursivement le YAML décodé des inclusions référencées dans le fichier YAML initial, il est recommandé d’utiliser uniquement la fonction yaml_decode_file() en précisant dans les options le chargement des inclusions.

En outre, le filtre decoder_yaml() qui encapsule yaml_decode_file() et yaml_to_array() pour les itérateurs ont été conservés. Le filtre decoder_yaml() peut bénéficier pleinement de l’argument optionnel $options mais pas yaml_to_array() qui structurellement ne peut recevoir que la chaine YAML en argument.

Les librairies YAML

Le plugin propose toujours les librairies de la branche v1, à savoir, Spyc et Symfony/yaml v1 mais permet aussi d’utiliser le composant YAML de la dernière version de Symfony v4 et l’extension PECL qui encapsule la librairie C libYAML.

Les versions de Spyc et Symfony/yaml v1 ont été mises à jour et les améliorations apportées par SPIP sur Symfony/yaml v1, reportées.

Chaque librairie requiert une version de PHP minimale et implémente un sous-ensemble propre des spécifications YAML 1.0, 1.1 ou 1.2. Le tableau suivant résume ces différentes contraintes et fournit la version actuelle - qui sera amenée à évoluer - de chaque librairie incluse dans le plugin YAML (version initiale 2.0.0).

LibrairieAlias*VersionPHP miniYAMLCommentaires
Spyc spyc 0.6.2 5.3.1 1.0 dépôt GitHub
Symfony/yaml v1 sfyaml 1.0.6 5.2.4 1.2 dépôt GitHub, archivé
Symfony/yaml v4 symfony 4.2 7.1.3 1.2 dépôt GitHub
libYAML libyaml 1.3.1 5.3.3 1.1 page PECL et dépôt GitHub libYAML
2.0.2 7.0.0

(*) : L’alias sert à désigner la librairie pour les API : valeur de $options['library'] ou de la constante _LIB_YAML.

Coté performance, on peut relever :

  • la librairie C libYAML est de loin la plus performante avec des benchmarks 6 à 15 fois plus rapides que les autres librairies ;
  • les librairies Symfony/yaml v1 et Spyc ont des performances comparables plutôt correctes ;
  • la librairie Symfony/yaml v4 est 2 fois plus lente environ que la version v1.

En outre, le sous-ensemble des spécifications YAML proposé par chacune des libraires varie beaucoup. Pour les besoins de SPIP qui concernent principalement des paramètres de configuration, toutes les librairies se valent quelque soit la version de spécifications YAML supportée. Pour se donner une vision des différences d’implémentation, une série de fichiers de tests (demo/$library.yaml) est fourni avec le plugin. Ces fichiers sont issus du fichier de tests natif de la librairie Spyc. Pour les autres librairies, il a été adapté en mettant en commentaire les cas non supportés.

Il faut noter que deux features YAML importantes ne sont pas implémentées par le plugin car pas disponibles pour toutes les librairies :

  • les 3 tirets de début et les 3 points de fin de document YAML,
  • et par conséquence, le multi-documents au sein d’un même fichier YAML : un fichier YAML doit toujours contenir qu’un seul document YAML.

Par défaut, pour assurer la compatibilité avec la branche v1, le plugin YAML utilise la librairie Symfony/yaml v1 (alias sfyaml), ce qui reste un bon choix.

Maintenance du plugin

Outre les corrections de bugs, la maintenance du plugin coïncide avec celle de ses librairies.

La librairie par défaut, Symfony/yaml v1 n’est plus maintenue. Elle n’évoluera donc pas mais elle fonctionne toujours très bien avec les spécifications et les utilisations actuelles du YAML. Elle est composée de 4 fichiers installés dans le répertoire sfyaml/ du plugin.

La librairie libYAML est distribuée en extension PECL. Elle s’installe donc sur le serveur Apache, rien n’est à faire au niveau du plugin. Elle est clairement à privilégier si l’installation est possible.

Les deux autres librairies, Spyc et Symfony/yaml v4, sont intégrées dans le plugin via Composer. On les retrouve sous le répertoire vendor/. Il est possible simplement de les mettre à jour voire de revenir à une version précédente en utilisant le fichier composer.json présent à la racine du plugin et en activant la commande composer update.

Utilisation avancée de l’API

Comprendre les options

L’utilisation basique des fonctions d’API n’a pas été modifiée. Par contre, l’ajout de l’argument $options en second paramètre de chaque fonction d’API permet une utilisation plus flexible et plus précise du plugin. Les options proposées sont résumées par fonction dans le tableau ci-dessous et un « x » précise si l’option est disponible pour une librairie donnée.

OptionExplicationsfyamlsymfonyspyclibyaml
Toute fonction
library Précise la librairie à utiliser indépendamment de la constante _LIB_YAML x x x x
yaml_decode() et yaml_decode_file()
include Traite les inclusions de fichiers YAML (true) ou pas (false, par défaut) x x x x
show_error Affiche les erreurs (true) ou pas (false, par défaut) x x
flags Combinaison des flags bitwise PARSE_* supportés par la librairie, 0 par défaut x
yaml_encode()
indent Nombre d’espaces pour chaque niveau d’indentation, 2 par défaut x x x x
inline Niveau à partir duquel la présentation du YAML devient inline, 3 par défaut x x
flags Combinaison des flags bitwise DUMP_* supportés par la librairie, 0 par défaut x

Décoder les inclusions

Le plugin YAML permet, depuis sa version v1, de faire des inclusions dans un fichier YAML sous la forme :

- 'inclure:dossier_relatif/fichier.yaml'

Cette fonctionnalité évite de répéter des listes de blocs identiques dans plusieurs fichiers YAML ce que la spécification YAML n’autorise pas nativement. Ce traitement des inclusions étant relativement couteux (parcours récursif de la structure de données issue du décodage), il n’est pas appliqué d’office lors du décodage du YAML mais demandé explicitement.

Avec la branche v1 du plugin, cette fonctionnalité est réalisée en appelant la fonction d’API yaml_charger_inclusions() sur le résultat du décodage d’un fichier ou d’une chaine YAML :

$configuration = yaml_charger_inclusions(yaml_decode_file($fichier));

La nouvelle branche v2 recommande une autre façon de réaliser ce traitement sans appeler la fonction yaml_charger_inclusions() qui se retrouve ainsi dépréciée, mais en utilisant une option de décodage comme illustré ci-dessous :

$configuration = yaml_decode_file($fichier, array('inclure' => true));

Forcer une librairie lors d’un appel

Si la librairie utilisée par défaut (constante _LIB_YAML) ne convient pas pour un appel précis, il est possible de forcer pour cet appel une autre librairie :

$configuration = yaml_decode_file($fichier, array('library' => 'libyaml'));

Visualiser les erreurs

Par défaut, pour la branche v2, les erreurs de décodage du YAML ne sont plus affichées. Pour forcer leur affichage il faut utiliser l’option ’show_error’ ainsi :

$configuration = yaml_decode_file($fichier, array('show_error' => true));

La suite...

  • réfléchir éventuellement à une logique de détermination plus évoluée pour choisir la librairie par défaut ?
  • déprécier certaines librairies pour ne garder que libYAML si installée ou l’une des librairies installées par composer ?

Discussion

12 discussions

  • 4

    Bonjour,

    La V2 est maintenant installée par défaut via SVP pour SPIP 3.2.
    Et elle fait planter le plugin ieconfig au moment de la restauration d’une sauvegarde.

    Un boolean à la place d’un array en ligne 56 de formulaires/ieconfig_import.php

    Par ailleurs, les config exportées avec la V2 font planter complètement l’importation si on remet Yaml en v1

    Bref, ça pose de gros problèmes.

    Je rebranche le zip sur la V1 en attendant en espérant qu’il n’y a pas trop de monde touché par ça...

    • Hello,

      Ton approche du problème m’interdit toute possibilité de correction.

      1. déjà tu aurais du modifier la borne mini de YAML v2 et pas supprimer le zip d’autorité car maintenant je ne peux plus tester le problème SVP si il y en a un.
      2. je suppose que tu parles de formulaires/ieconfig_import.php dans le plugin IEConfig ?
      3. quel est ton jeu de test pour que je puisse reproduire (ou pas) ton souci ?
      4. quelle librairie YAML utilises-tu ? c’est essentiel car comme je l’ai expliqué dans l’article, elles ne supportent pas toutes le même ensemble de features YAML.
      5. Pour la production des YAML je te redis une deuxième fois que comme expliqué les librairies ne sont pas équivalentes ni en import ni en export. Et comme tu as du produire ton YAML avec YAML tu dois avoir des --- au début que la librairie Symfony ne sait pas lire. Mais ça il faudrait que tu me donnes des détails sur le fichier et la librairie que je n’ai pas.

      En conclusion, si on veut avancer il faut faire tout le contraire de ce post. Je suis désolé d’être faché mais passer des heures sur ce plugin pour en arriver là est frustrant. Donc je veux bien corriger mais il faudrait que je sache quoi !

    • Hello,

      Je vais essayer de répondre à toutes tes questions.

      1. je ne vois pas ce que changer compatibilite=« [3.0.0 ;3.2.*] » aurait pu modifié du comportement de SVP (Ajouter un plugin, rechercher yaml, constater qu’il n’y a qu’un seul plugin yaml proposé, l’instaler, c’est la version 2.0.2)
      2. oui
      3. jeu de test : export avec ieconfig de n’importe quelle config, tentative d’import infructueuse
      4. librairie par défaut à l’installation de Yamlv2, rien dans config/mes_options.php
      5. les fichiers ont été supprimés car, après avoir compris le problème, j’ai installé manuellement la v1 de Yaml et constaté qu’elle bugait avec les .yaml des sauvegardes de ieconfig faites avec la V2

      En conclusion, ayant constaté que pour un néophyte (en l’occurrence un stagiaire découvrant SPIP), il se retrouvait dans une situation problématique, je me suis dit que le problème allait concernait rapidement tous ceux qui mettaient à jour leurs plugins par SVP (pour mémoire, formidable et ieconfig sont impactés), j’ai préféré éteindre le départ d’incendie.

      Mais je comprends que ça puisse te mettre en colère. Et j’en suis désolé.

    • Yo,

      Merci pour les réponses.

      1. ben en mettant la borne mini à strictement supérieure à 3.2.* alors tu n’aurais pas eu la proposition dans SVP car le zip aurait été incompatible avec une 3.2. Mais on aurait toujours le zip et on pourrait tester cette version en 3.3.0-dev
      2. ok
      3. je n’utilise jamais ieconfig. Quels plugins peuvent me permettre de faire ces tests ?
      4. ben là je suis dubitatif car dans un autre message tu m’avais dit que tu avais installé libYAML ?
      5. ben là aussi je suis dubitatif car si tu utilises la librairie par défaut de v2 c’est la même que la v1. Or on a pas le problème en v1 ?

      Bon j’essaye de regarder un peu cela demain après-midi et si tu es sur IRC je te ferais signe.

    • Hello,

      Avec plaisir.

      1. Est-ce que tu veux que je le fasse ?
      2.  ;-)
      3. ieconfig n’a pas besoin de plugins : il y a toute la config du core qui est gérée par ce dernier, donc, tu peux tester avec un SPIP sans plugin autre que ieconfig et ses dépendances
      4. c’est bien pour ça que j’ai fait un nouveau thread ;-) Dans ce thread, c’est une installation vierge, sans define, juste ce qui se produit par défaut à l’installation
      5. la lib par défaut en V2 rajoute 2 espaces en début de chaque ligne des yaml générés (mais je viens de voir que tu as corrigé ça en https://zone.spip.org/trac/spip-zone/changeset/110648)

      Je vais essayer de garder un œil sur IRC ;-)

    Répondre à ce message

  • 5

    À propos de textwheel et pour info, j’ai ré-écrit textwheel sous forme de library standard il y a 2/3 ans, ça peut s’intégrer dans n’importe quel framework/cms qui utilise Composer.

    L’idée suivante aurait été de faire un plugin SPIP, dans le cas où SPIP utiliserait Composer, qui requiert cette librairie d’une part afin que textwheel vive sa propre vie (et d’assurer l’interopérabilité souhaitée par ses auteurs au début http://zzz.rezo.net/Presentation-de-Textwheel.html) et les règles SPIP d’autre part, en supposant que les règles soient distribuées sous forme de composant (par composer, donc)

    Le plugin textwheel pour spip ne serait qu’un wrapper.

    • Hello,

      Oui je me rappelle très bien de l’objectif de TextWheel d’être « indépendant » de SPIP. C’est d’ailleurs pour cela qu’il embarque une version mini de la vieille librairie YAML de symfony v1.

      Et c’est bien mon propos. Pourrait-on aujourd’hui envisager d’utiliser le plugin YAML dans la dist de SPIP avec un TextWheel qui se branche dessus surtout qu’avec l’utilisation de libYAML on améliore les performances d’environ un facteur 10 ?

    • Erreur ! :) TextWheel embarque un bout de lib d’un autre projet parce que SPIP ne permet pas d’intégrer des librairies autonomes de manière standard. D’autre part, c’est un plugin SPIP qui n’est pas autonome (et ne peut le devenir en l’état) puisqu’il dépend encore de fonctions du noyau de SPIP, me semble-t-il. C’était en tout cas vrai quand j’ai isolé la partie ./engine à l’époque.

      Embarquer un nouveau plugin SPIP dans la « dist », je ne sais pas si c’est une bonne ou une mauvaise idée. Tout dépend de comment et à quelle vitesse la communauté spipienne adopte Composer (ou pas) pour le développement de SPIP, de ses plugins, voire de librairies standard ...

      Si ça ne tenait qu’à moi, j’éviterais les surcharges par des fonctions publiques des appels à l’API de la librairie symfony/yaml, et j’introduirais l’autoloading PSR-4 dans SPIP. Mais ça, je n’ai pas à le décider tout seul. ;-)

    • Ouais bof, je ne suis pas convaincu de mon erreur...
      Dans ce cas pourquoi TextWheel redéfinit une fonction qui existe déjà un autre plugin ? En plus, on se pose la question de quelle librairie est appelée quand YAML est activé. Non je n’en vois pas l’intérêt surtout que l’évolution de la librairie n’est jamais prise en compte.

      Cette question revient sur le tapis à chaque fois que l’on propose un nouveau plugin. J’ai toujours du mal à comprendre cette logique de pseudo stabilité des plugins-dist qui ne répond à aucune stratégie claire de mon point vue. Ma vision du noyau est plutôt celui d’un framework que celui d’un coeur fonctionnel (toute proportion gardée). A cet égard manipuler des fichiers (XML, JSON, YAML...) c’est du noyau comme les raccourcis SPIP ou les itérateurs. Après les plugins externes c’est pour des ajouts fonctionnels. On a l’impression de vouloir définir la dist de SPIP comme un SPIP vision historique immuable (je caricature un peu).

      Enfin, la question que je soulève est simple : on a un plugin dans la dist qui utilise une fonction qui est proposée dans un autre plugin qui n’est pas dans la dist : est-ce logique et si non que pouvons nous faire ?

    • Ouais bof, je ne suis pas convaincu de mon erreur...

      Tu expliques que c’est pour être « indépendant » que le plugin « embarque » une portion de code maintenue ailleurs, alors que c’est parce que SPIP ne gère pas les dépendances avec un système commun avec cet ailleurs que cette portion de code a été dupliquée.

      Par ailleurs, et j’ai pu me méprendre, j’ai toujours cru que la volonté d’indépendance de TextWheel par rapport à SPIP résidait dans le fait que la méthode avait vocation à être utilisée ailleurs que dans SPIP. Donc, sans nécessiter une partie de son code et par voie de conséquence d’un SPIP tout entier.

      pourquoi TextWheel redéfinit une fonction qui existe déjà un autre plugin ?

      Je demanderais directement aux auteurs de ces plugins, si j’étais toi.


      Quoiqu’il en soit, ne nous chamaillons pas, il me semble qu’on aspire peu ou prou à la même chose, c’est juste la méthode qui diffère :

      • Tu dupliques du code (symfony/yaml) dans un plugin SPIP packagé via smart_paquets, parce qu’aujourd’hui, c’est comme ça que ça marche.
      • Je propose qu’on change de méthode, démo à l’appui, pour que SPIP puisse s’appuyer quand c’est nécessaire sur du code conçu et maintenu ailleurs sans qu’il soit dupliqué dans un plugin SPIP, par le biais d’un système de gestion de dépendances commun à l’ensemble de la communauté PHP.

      Et je propose ce changement parce qu’il me semble être une meilleure solution à la plupart des problèmes que tu soulèves, considérant que c’est précisément le packaging « proprio » de SPIP qui engendre ces problèmes.

    • Tu expliques que c’est pour être « indépendant » que le plugin « embarque » une portion de code maintenue ailleurs...

      Je crois que mon expression n’était pas assez précise parce que je voulais dire exactement ce que tu écris dans ta réponse. Si Textwheel nécessitait YAML il serait plus difficile à utiliser en dehors de SPIP.

      Tu dupliques du code (symfony/yaml) dans un plugin SPIP packagé via smart_paquets, parce qu’aujourd’hui, c’est comme ça que ça marche...

      Là c’est un peu réducteur car l’encapsulation de la librairie n’est pas toujours une redite exacte de l’API proposée par ladite librairie. Elle peut avoir une existence propre et donc un intérêt.

      Après, l’inclusion de symfony/yaml v4 et Spyc est faite via composer. C’est pas ultime car tu ne mettras pas à jour le site de production avec mais ça permet au moins de mettre facilement à jour le plugin. J’ai galéré un maximum pour retrouver la version et la trace de la librairie sfyaml quand j’ai commencé mes investigations. Un petit pas...

      Après pour finir avec Textwheel je pense qu’il faut migrer les YAML en JSON, ce qui se fait en quelques secondes et permet de s’affranchir complètement de toute librairie et n’utiliser le YAML que pour des configurations ou des exports plus « grand public ».

    Répondre à ce message

Ajouter un commentaire

Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :

  • Désactiver tous les plugins que vous ne voulez pas tester afin de vous assurer que le bug vient bien du plugin X. Cela vous évitera d’écrire sur le forum d’une contribution qui n’est finalement pas en cause.
  • Cherchez et notez les numéros de version de tout ce qui est en place au moment du test :
    • version de SPIP, en bas de la partie privée
    • version du plugin testé et des éventuels plugins nécessités
    • version de PHP (exec=info en partie privée)
    • version de MySQL / SQLite
  • Si votre problème concerne la partie publique de votre site, donnez une URL où le bug est visible, pour que les gens puissent voir par eux-mêmes.
  • En cas de page blanche, merci d’activer l’affichage des erreurs, et d’indiquer ensuite l’erreur qui apparaît.

Merci d’avance pour les personnes qui vous aideront !

Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.

Qui êtes-vous ?
[Se connecter]

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

Ajouter un document

Suivre les commentaires : RSS 2.0 | Atom