51

Re : Hoa_Xyl vient d'apparaître dans le tronc

Hey hey smile,

Voici l'implémentation des overlays dans XYL. Il ne manque pas grand chose, le principal est là et ça marche vachement bien (c'est beau à voir, j'vous jure !) smile.

L'idée est la suivante.
On a un fichier du genre :

<?xml version="1.0" encoding="utf-8"?>
<?xyl-use href="contacts.xyl"?>

<page>
  <p>
    <ul id="z" foo="bar" class="ye">
      <li>Doudi</li>
      <li>Titine</li>
      <li>Toto</li>
      <li>Tata</li>
    </ul>
  </p>

  <contacts bind="?foo" />
</page>

Pas de soucis jusque là, on connaît bien. On retrouve notre <?xyl-use?> (maintenant obligatoirement placé en en-tête) qui devrait sûrement contenir la définition des différents composants (notamment <contacts />). Mais on a ajouté du code XYL écrit en dur : la première ul#z.

Sauf que voilà, cette liste on aimerait bien la modifier. Par exemple : on livre une application qui permet d'embarquer des modules. Prenons un exemple vraiment simple à comprendre : un blog. Et vous êtes le développeur d'un module/extension pour ce blog. Vous voulez dire : « je veux ajouter un élément dans cette liste mais sans avoir à toucher le code de base, sinon, à chaque mise à jour mon travail est foutu ! ». On réfléchit … Comment faire ?
Tada, on a les overlays.

On écrit quoi alors ?
On va écrire un fichier (overlay.xyl par exemple) :

<?xml version="1.0" encoding="utf-8"?>

<overlay>
  <ul id="z" tic="toc" class="pee">
    <li id="zibzib" position="last() - 2">Azizti</li>
    <li>Hobbi</li>
  </ul>
</overlay>

Ça dit quoi ? On a un overlay. À l'intérieur, on a un élément avec un identifiant. On va chercher cet identifiant dans le document initial. S'il existe, on va faire un merge des deux, donc on va commencer par ajouter les attributs. Ici on notera qu'on aimerait bien que les classes s'additionnent (on a "ye" et "pee" ici, on aimerait que ça donne "ye pee").
Ensuite à l'intérieur, on a d'autres éléments. Le premier comporte un identifiant. On le cherche dans le sous-arbre de z et on ne le trouve pas. Bien, soit. On le positionne alors ! Sa position est : « à partir du dernier moins deux ». Bien. On trouve le dernier élément et on le place 2 éléments avant.
Enfin, le dernier élément n'a pas d'identifiant, ni de position, donc on l'ajoute à la suite des éléments déjà existants.

Ensuite, on va essayer de lier notre document initial avec notre overlay. On s'y prendrait donc de cette façon :

<?xml version="1.0" encoding="utf-8"?>
<?xyl-use href="contacts.xyl"?>
<?xyl-overlay href="overlay.xyl"?>

<page>
  <p>
    …

Vous voyez la différence ? On ajoute <?xyl-overlay?>. Simple.
Et le résultat ce sera quoi ? On va mouliner tout ça et faire un rendu HTML5, soit :

<!DOCTYPE html>

<html>
<body>
<p>
  <ul>
    <li>Doudi</li>
    <li>Titine</li>
    <li id="zibzib">Azizti</li>
    <li>Toto</li>
    <li>Tata</li>
    <li>Hobbi</li>
  </ul>
</p>

<h1>Foobar</h1>
…

On remarque bien que les nouveaux éléments dans le menu ce sont positionnés ! C'est magique smile.

Je trouve ce mécanisme vraiment très pratique. Notez que je n'ai rien inventé : j'ai juste implémenté les overlays XUL dans XYL, avec des petits plus au niveau positionnement (les opérations arithmétiques ne sont pas possibles dans XUL).
Il faudrait ajouter un manifest pour faciliter les inclusions des overlays, comme ça on toucherait encore moins au fichier principal. Mais je dois réfléchir à ça, savoir si ce serait aussi pratique que je ne le pense.

Revenons quelques secondes sur l'attribut position.
Il peut prendre des valeurs entières, tout simplement, pour expliciter une position. Ainsi :

<tag position="7" />

est valide.
Mais on peut aussi faire des opérations (arithmétiques du premier ordre sur les naturels), donc l'addition, la soustraction, la multiplication et la division (entière ou flottante. Donc :

<tag position="7 * 7 - 3.5 * 2" />

On peut aussi placer notre élément par rapport à un dernier élément, grâce à la fonction last(). On peut donc combiner tout ça comme on veut :

<tag position="last() - last() + 1" />

Oui, autant faire 1 directement, mais ce serait moins drôle wink.

Ce qu'il me reste à faire c'est placer un élément par rapport à un identifiant d'un autre (et j'aimerais que les opérations fonctionnent également avec). Un truc du genre : position(someID) + 7 par exemple. Je ne sais pas si la forme serait correcte. Il faut que je regarde par rapport à XPath 2.0 et les CSS Query Selectors pour ne pas faire de bêtises.

Petite note avant de terminer : pour ceux qui ont le doute, on peut utiliser les yield pour produire du code XYL, puis appliquer un overlay sur ce code fraîchement produit, il n'y a aucun problème. Les overlays s'appliquent après computation des yields.

Alors, vous en pensez quoi smile ?

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

52

Re : Hoa_Xyl vient d'apparaître dans le tronc

Juste  : big_smilelol tongue et cool

Apprend Hoa et est heureux smile

53

Re : Hoa_Xyl vient d'apparaître dans le tronc

Hey smile,

Les overlays peuvent maintenant inclure d'autres overlays grâce à l'instruction <?xyl-overlay?>, normal, classique :

<?xml …?>
<?xyl-overlay href="otherOverlay.xyl"?>

<overlay>
  …

Les <?xyl-use?> servent à inclure des définitions. Les <?xyl-overlay?> servent à inclure des overlays.

J'ai aussi amélioré pas mal les performances et je prépare une nouvelle façon d'interpréter XYL. Ce serait plus rapide et plus efficace normalement.
J'ai corrigé un bug qu'avait soulevé Metagoto avec des statiques pas très propres. Il me reste encore un bug à résoudre mais il est corriace (je ne trouve pas de solutions alternatives).

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

54

Re : Hoa_Xyl vient d'apparaître dans le tronc

J'ai oublié de préciser que l'attribut position des overlays peuvent maintenenant utiliser la position d'un élément (grâce à son ID).

Ainsi :

<element position="last() - element(#anId) + 2 * 2.5" />

fonctionne.

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

55

Re : Hoa_Xyl vient d'apparaître dans le tronc

Bien. Après trop de jours passés dans le code source C de SimpleXML, dans l'implémentation du DOM et dans la libxml, je jète l'éponge. J'ai cru avoir trouvé une solution pour interpréter un arbre SimpleXML mais finalement des restrictions à la con dans PHP m'en empêche. J'ai essayé de faire ça proprement pour économiser du calcul et de la mémoire, mais il n'existe aucun moyen propre pour le faire.

J'ai plusieurs benchmarks et j'ai choisi la meilleure solution (la plus rapide et la moins coûteuse en mémoire) en conséquence. Elle apparaîtra dans le tronc d'ici quelques jours (sûrement ce week-end, j'aurai du temps pour finir de la coder) et ça fera considérablement avancer XYL.

Si jamais certains savent régler ce problème, ils sont les bienvenus : http://bugs.php.net/52889. L'idée c'est qu'un arbre SimpleXML ne peut pas contenir d'informations (impossible d'avoir nos propres attributs car ils sont reconnus comme des enfants XML). J'ai donc développé un store mais c'est assez sale. Et surtout ça pose des problèmes pour la suite car j'y fais souvent des accès. L'idée était donc d'interpréter l'arbre SimpleXML pour avoir un arbre concret (a contrario d'abstrait, comme l'est l'arbre SimpleXML).
Pour vraiment parler simplement : au lieu d'avoir uniquement des nœuds SimpleXMLElement, je voulais des nœuds A, B, C, Foo, Bar, Baz, Qux etc. Des objets qu'on peut écrire nous-même. Seulement, SimpleXML est vraiment plus rapide que SAX, DOM, XMLReader, XML Parser (qui serait idéal sur le coup) etc. Et SimpleXML a un avantage, c'est qu'il donne une vision « simple » d'une ressource libxml, … tout comme DOM donne une vision « DOM » de cette même ressource XML ! Donc si on manipule la même ressource avec SimpleXML ou DOM, les modifications sont « répliquées ». Je ne sais pas si je suis très clair …

Je veux conserver ces avantages. Je n'ai pas le choix donc, je pars sur ma première idée d'un arbre « fantôme », une sorte de clone. On aura donc deux arbres.
Dans l'idée c'est pas mal. Si on veut analyser un fichier XML rapidement, ou le manipuler très facilement, on a les classes Hoa_Xml_Read, _Write etc _ReadWrite. Mais si jamais on veut l'interpréter, on aura Hoa_Xml_*::interprete() qui nous retournera notre arbre fantôme. Il sera facile de passer de l'un à l'autre (de concret à abstrait et inversemment).

Cette solution ne m'enchante guère, mais je ne peux pas faire mieux dans l'état actuel des choses.

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

56

Re : Hoa_Xyl vient d'apparaître dans le tronc

J'ai réécris pas mal de code aujourd'hui et j'ai beaucoup avancé. Pas encore totalement commité, mais je peux maintenant interpréter l'arbre abstrait XYL en arbre concret. Gain de performance : 8%. Je peux encore faire mieux. J'y travaille !

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

57

Re : Hoa_Xyl vient d'apparaître dans le tronc

Hey les amis (et amies, desfois que …) !

J'ai une énorme nouvelle aujourd'hui. J'ai réussi ce à quoi je rêve depuis un bon moment et vous aussi (sans le savoir peut-être wink).

Vous avez toujours écrit une balise (un composant dans XYL) dont la valeur dépend des balises plus loin dans le code ? Mais si voyons. Rappelez-vous tous <title>. Sa valeur ne dépend-t-elle pas d'un <h1> quelque part ? Mais il y a mieux encore : un sommaire. Vous avez toujours rêvé d'écrire votre sommaire et qu'à chaque fois qu'on ajoute un élément de section ou de titre (<section> ou <hn>), il apparaît automatiquement dans ce sommaire, affiché en amont de vos éléments de section ou de titre ? C'était une vraie galère hein wink ? Bah c'est fini maintenant, hop, on fait péter le champagne !

Je viens de modifier la manière dont XYL est exécuté (une de ses phases d'exécution du moins). Je me suis bien amusé avec les calculs de complexités et je pense avoir fait le meilleur choix.

Vous savez maintenant que l'on a deux arbres : un arbre abstrait (notre arbre XML) et un arbre concret (notre arbre XYL). Chaque nœud de notre arbre concret représente une classe que l'on a écrit nous même, avec ses propres méthodes, attributs etc. C'est très important ça. Ça permet que chaque élément porte des méthodes lui permettant d'interagir avec d'autres éléments.

Maintenant j'en reviens au traitement d'un arbre XYL. On commence par ouvrir notre fichier. On traite les <?xyl-use?>, les <yield> et les <?xyl-overlay?>. Ensuite on transforme cet arbre abstrait en arbre concret. Chaque élément XYL est associé à une classe pour un interpréteur donné. Un interpréteur c'est par exemple : HTML5, PDF, Text etc. C'est une façon de traiter/d'interpréter XYL.
Une fois qu'on a notre arbre concret, on le parcours 2 fois (uniquement 2 fois, c'est là que c'est … émouvant smile). Le premier parcours sert à traiter les liages de données (data binding). Le second parcours est le rendu, on va dessiner notre document.

Revenons quelques instants sur notre premier parcours. On a bien un parcours pour lier les données, donc elles ne sont pas sur l'arbre, mais seulement des références vers le saut à données (data bucket). C'est plus léger et plus facile. C'est bien entendu récursif. Et lorsqu'on a fini de traiter un élément et ses enfants, on l'exécute. C'est ce que j'appelerais de l'exécution ascendante. En effet, on exécute des nœuds/enfants les plus profonds vers les nœuds/parents les plus hauts (jusqu'à revenir à la racine). On est dans la direction : feuille-racine. L'exécution s'effectue en remontant la récursivité.
Mais attention, l'exécution ne concerne qu'un seul élément et ne doit pas (ne devrait pas du moins) se propager vers ses enfants. Pourquoi ? Car ses enfants ont été exécutés avant lui déjà (on est en ascendant) et que chaque élément/composant doit pouvoir fonctionner seul. S'il a besoin de ses enfants, c'est qu'il y a une trop grosse dépendance, ce n'est pas bon. Donc c'est axiomatique : notre exécution ne concerne qu'un composant.

L'exécution permet d'effectuer des tâches. Comme par exemple : lire les données (c'est déjà possible, si si, le liage est opérationnel car on est en ascendant, le liage s'étant fait en descendant). Je reprends l'exemple de mon sommaire : à chaque exécution, tous les éléments de section ou de titre peuvent dire : je cherche l'élément <tableofcontents /> par exemple (si on veut l'appeler ainsi) grâce à l'API de Hoa_Xml, et j'appelle une de ses méthodes permettant d'ajouter une ligne au sommaire, comme : addEntry($level, $id, $label) (je précise que ce n'est qu'un exemple … pour l'instant wink).

Ça apporte quoi de plus qu'un bon vieux scan de fichier à la con ? Bah c'est juste hyper propre et ça ne se limite surtout pas à ce genre d'usage. Tous les éléments peuvent interagir entre eux. Donc possibilité d'ajouter des <link /> ou <script> à la balise <head> très facilement. Il suffirait que lors de l'exécution d'un composant nécessitant un fichier Javascript, on cherche notre élément porteur de l'en-tête du document et qu'on lui demande d'ajouter un lien vers le dit fichier. Facile non ?

Et le tout en seulement deux parcours ! Je suis très content de moi. La logique me paraît très bonne, le code est hyper beau. J'en pleurerais presque smile.

En revanche (il y a toujours un « mais »), pour un document avec 2 <?xyl-use?>, un <?xyl-overlay?>, des trucs assez tordus etc., j'ai 23ms dans la vue. Sur ces 23ms, j'ai estimé à 22ms d'inclusion de fichier et 1ms de réels calculs par PHP. J'évite autant que possibles les accès disques mais je ne peux pas faire autrement. Il existe des extensions à PHP pour mettre de l'opcode en cache assez efficacement (j'en ai en tête), ça peut contourner le problème. Je trouve que 23ms est juste trop trop long. Bon après il faut voir l'ensemble de l'application avec des caches, un reverse-proxy, un cache-HTTP, un accélérateur d'opcode, un cache d'opcode etc. Mais pour l'instant, c'est le seul hic à mon goût. Et au votre ?

Sur ce, je vais commiter tout ça, et je m'attaque à l'intégration des espaces de nom dans Hoa_Xml et donc Hoa_Xyl pour permettre des choses encore plus folles. Je ne vous en dis pas plus wink.

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

58

Re : Hoa_Xyl vient d'apparaître dans le tronc

Sympa.
Je viens de regarder le code rapidos. Il n'y a pas d'exemples ni tests pour l'exécution des concrete elements (d'ailleurs, tu ne distribues jamais de tests sur ton repo svn?). Les concrete elements disponibles ne définissent que paint(). Si je comprends/suppute bien, dans le cadre de ta présentation juste au dessus, si un concrete veut opérer sur <tableofcontents/>, il le fait via l'api des abstract elements pour obtenir ensuite le concrete ciblé. Ca donnerait: concrete --> abstract --> bidouille (selection) au niveau des abstracts --> concrete. C'est bien ça?

D'accord pour l'exécution ascendante (ça apparait clairement dans le code), mais ne penses-tu pas qu'il puisse y avoir des cas "problématiques" lorsque l'ordre d'apparition de 2 elements sibling dans le document ne correspond pas à l'ordre souhaité d'exécution?

59

Re : Hoa_Xyl vient d'apparaître dans le tronc

Je ne commit que très peu d'exemples. J'ai un « laboratoire » (un champ de mines surtout tongue) en local.
Oui, il n'y a que paint() pour l'instant, mais il y a un execute() parent vide. Tu veux un exemple ? Je peux t'en recoder un rapidement.

Et oui pour les aller-et-retours entre concret et abstrait. Lorsqu'on veut sélectionner un élément, on va faire un xpath() ou un querySelector() sur l'arbre abstrait en partant de la racine (c'est le plus simple et le plus rapide), puis on prend les nœuds concrets à partir du résultat. Ça donne un truc du genre :

// on est sur un nœud concret,
// on récupèré donc son nœud abstrait.
$abstract = $this->getAbstractElement();

// on récupère la racine du document.
$root = $abstract->selectRoot();

// on fait notre sélection.
$foo = $root->xpath(…) or querySelector(…) or select*(…);

// on va ensuite repasser du bon côté (concret).
$concrete = $this->getConcreteElement($foo…);

// et on appelle notre méthode.
$concrete->doSomething(…);

Quatre étapes élémentaires je dirais.

Je n'ai pas compris ta problématique à propos de l'éxécution.

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

60

Re : Hoa_Xyl vient d'apparaître dans le tronc

Hywan a écrit:

Je n'ai pas compris ta problématique à propos de l'éxécution.

Comme le data binding est effectué en depth-first et l'exécution en remontant, que penses-tu de ceci:

A et B sont sibling:

Root
  A
  B

Si A veut opérer sur les data de B lors de l'exécution: problème?

Au pire, on pourrait ajouter des elements en fin de document et n'ayant aucun rendu. Leur seul but serait d'être exécuté.