SPIP-Contrib

SPIP-Contrib

عربي | Deutsch | English | Español | français | italiano | Nederlands

286 Plugins, 197 contribs sur SPIP-Zone, 259 visiteurs en ce moment

Accueil > Documentation > Tutoriaux pour le code de SPIP > Archives tutoriaux code SPIP > La genèse du nouveau compilateur > Une refonte du noyau de SPIP, optimisant son interface avec son langage (...)

Une refonte du noyau de SPIP, optimisant son interface avec son langage d’implémentation, ses serveurs Web & SQL, et son système d’exploitation (V11)

Archive historique

20 juin 2004 (Date de rédaction antérieure : 20 juin 2004).

Ceci est une archive périmée mais qui reste intéressante, parfois autant pour l’article que les commentaires associés.

ATTENTION : l’essentiel de cette contribution a été portée sur le CVS officiel de Spip.
Merci de consulter la version CVS, et son nouveau complément (article 639 et de se manifester sur spip-dev ou le forum de ce nouvel article.

Le travail de près d’un an ici décrit répond à plusieurs besoins, personnels ou exprimés sur spip-dev. Malgré une grande diversité d’interventions,
un fil conducteur l’a motivé : le modèle d’exécution de Spip.
Celui-ci consiste en la chaîne de traductions suivante :

Squelettes en Spip --1--> Script en PHP --2--> Page en HTML & JavaScript

Mais l’implémentation actuelle intercale dans ce modèle théorique
une phase 2bis consistant en
une deuxième passe de PHP appliqué au résultat de la phase 2,
afin d’y exécuter quelques balises <? qui s’y trouvent
parfois. C’est d’autant
plus gênant que le mécanisme de cache intervient après la phase 2,
et non après la phase 2bis, ce qui conduit le serveur Web à recommencer
à chaque requête une analyse du résultat de la phase 2.

En cherchant une nouvelle implémentation évitant ce travers sans diminuer les fonctionnalités de Spip, on a en fait abouti à une augmentation de celles-ci,
et à un meilleur système de cache. L’implémentation proposée parvient à respecter le modèle théorique pour la
plupart des squelettes standard. Les exceptions concernent quelques balises qui
n’ont pas encore été réimplémentées selon le modèle théorique
(FORMULAIRE_INSCRIPTION FORMULAIRE_ECRIRE_AUTEUR
FORMULAIRE_SIGNATURE FORMULAIRE_SITE
MENU_LANG MENU_LANG_ECRIRE LOGIN_PRIVE LOGIN_PUBLIC URL_LOGOUT
)
Bien entendu, les squelettes
contenant des balises <? font aussi exception.
Les réécrire pour profiter des nouvelles
fonctionnalités n’est pas une obligation, mais elle peut en valoir la peine :
la nouvelle version du squelette standard nouveautes, fournie en complément,
est moitié plus petite que l’originale, en plus de s’exécuter plus vite.

Relation avec le serveur de base de données

Ce point a été le démarrage de ce travail et a été décrit dans d’anciennes contributions, à l’exception d’une nouvelle fonctionnalité dans les accès aux champs d’une part, et d’une interface permettant de paramétrer leur traitement
par le traducteur Spip vers PHP d’autre part. En voici la liste complète.

-  reconnaissance des champs dans les critères de boucle. Par exemple, le critère {titre=#TITRE} sert à trouver dans la base un autre objet ayant même titre que l’objet de la boucle englobante. Cette absence avait provoqué l’introduction du critère « exclus » qui, avec cette innovation, peut s’exprimer tout simplement par {id_article !=#ID_ARTICLE}.

-  tout champ des tables SQL
est accessible par la syntaxe standard #nom du champ en majuscules ;
cette accessibilité fonctionne aussi bien pour le corps de la boucle
que pour les critères. Par exemple,

<BOUCLE0(ARTICLES){visites>5}{par visites}>#TITRE #VISITES<br/>
</BOUCLE0>

liste par nombres de visites croissants
tous les articles ayant plus de 5 visites, avec leur score.

-  la balise boucle peut s’appliquer non seulement aux tables standards de Spip
(ARTICLES, RUBRIQUES, etc) mais à
toute table accessible par la connexion au serveur SQL et préalablement déclarée
au traducteur PHP.
De la sorte,
s’il existe une table nommée matable1 ayant des champs nom et adresse, on peut écrire dans un squelette :

<BOUCLE_1(matable1)><br />#NOM #ADRESSE</BOUCLE_1>

et obtenir ce que les habitués de Spip devineront sans peine. La déclaration au
traducteur PHP consiste à allonger le tableau
tables_principales
présent dans le fichier inc_serialbase
qui décrit déjà les tables standards :
il suffit de s’en inspirer. Ce fichier est également lu à l’installation de Spip
pour effectivement déclarer les tables SQL si elles ne l’étaient déjà.

-  les balises Spip, du point de vue de l’implémentation sont de deux sortes :
celles qui sont exactement la valeur d’un champ d’une table SQL,
et celles qui résultent d’un calcul en PHP.
Il est possible d’étendre ce jeu de balises simplement
en déclarant dans le fichier mes_fonctions une fonction
nommée calcul_champ_nom_du_champ qui doit retourner le code PHP
à insérer dans le squelette compilé (cette fonction sera appelé même
s’il existe un champ homonyme dans une table SQL — que cette fonction
pourra consulter si elle y tient).
On trouvera dans le fichier inc-index-squel les spécifications précises.

-  Spip permet de référencer à l’intérieur d’une boucle un champ d’une boucle englobante, sauf en cas d’homonymie ; cette restriction est ici levée grâce
à la syntaxe nom_de_boucle :nom_de_champ, admise dans le corps comme
dans les critères. Par exemple :

<BOUCLE0(RUBRIQUES)><BOUCLE1(ARTICLES){id_rubrique}>#0:TITRE  ...

référencera le titre de la rubrique, pas de l’article.

-  les paramètres comme 1,n-1 où n désigne le nombre d’éléments issus de la requête (voir les spécifications) sont reconnus.

-  lorsqu’un même champ est comparé (avec =) plusieurs fois à une constante on synthétise une clause IN et on s’assure que toutes les valeurs ont été rencontrées. Par exemple l’utilisation simultanée des deux critères
id_mot=16id_mot=2 permettent de repérer un objet ayant
deux mots-clés (ou plus) ayant ces valeurs
(normalement les critères de Spip sont réunis par un « ET » logique,
mais comme ce serait ici clairement impossible, on prend exceptionnellement
un OU). Ceci solutionne le bug 168 posté sur spip-commit, mais nécessite
des requêtes imbriquées, que MySQL n’offre qu’à partir de la 4.1.

-  tout ceci est réalisé dans un encombrement minimum du processus car le code du squelette est entièrement analysé avant
la production de la requête SQL, qui se limite alors aux seuls champs effectivement
utiles. Chaque requête retourne ainsi une sorte d’environnement lexical (à la Scheme
pour les connaisseurs) ne nécessitant aucune recopie dans des variables globales
ou autres. Cette requête est construite par une fonction spécifique intégrant plus en amont le préfixe des tables,
et qui devrait faciliter le portage de Spip sur d’autres serveurs SQL.

En bref , la généralisation de l’usage des champs doit raréfier le besoin
de recourir à PHP pour en mémoriser les valeurs.

Relation avec le langage d’implémentation

Toujours dans l’optique d’éviter les inclusions de PHP autant que possible,
il est possible à présent :

-  de référencer dans un critère de boucle les valeurs des variables passées dans l’URL ou dans la liste des variables de la balise INCLURE lorsqu’il s’agit d’un squelette inclus. Ainsi, avec le critère
{titre==$regexp}dans un squelette nommé partitre, l’URL
partitre.php3?$regexp=*refonte*
retournera les articles comportant le mot refonte dans leur
titre. De même, le critère
{par $tri}
permet d’indiquer à travers l’URL l’ordre dans lequel les éléments de la table
vont être affichés.

-  les champs étendus peuvent être imbriqués :

[ 1 (#DESCRIPTIF) 2 [ 3 (#SURTITRE) 4 ] ]

ne produira rien si DESCRIPTIF est vide,
même si SURTITRE ne l’est pas.

-  la syntaxe nom de champ|filtre{argument2, argument3... } n’acceptait que des constantes comme arguments ;
elle accepte à présent des noms de champs (qui seront substitués à leur valeur)
et des variables passées dans l’URL ou la balise INCLURE. De la sorte, l’interpolation
dans un squelette de :

<?php mafonction('#TITRE', '#CHAPO', $_GET['monparam']) ?>

peut être remplacée par

[(#TITRE|mafonction{#CHAPO,$monparam})]

avec l’avantage que cette fonction sera appelée une seule fois,
son résultat
étant mis en cache dès la fin de l’exécution du squelette,
alors que l’autre écriture provoque un calcul postérieur
à l’action
du système de cache, n’en profitant donc pas.

-  la logique de cette raréfaction des balises

<?<code> suggère que chaque squelette
soit décrit par 2 fichiers, l'un contenant exclusivement du HTML et les balises
Spip comme celle-ci, et l'autre contenant les fonctions PHP appelées à partir
des filtres; c'est pourquoi cette extension de Spip, lorsqu'elle doit exécuter
un squelette  {s} charge un éventuel fichier {s}<code>_fonctions

.
Par exemple, pour le squelette rubrique=22.html
on chargera
rubrique=22_fonctions.php3
s’il existe.

L’ancienne écriture reste disponibe car
il existe des cas où le recalcul systématique est nécessaire,
typiquement si l’on veut tenir compte de l’identité du demandeur. Hormis dans ces cas, la nouvelle
écriture est à préférer.

Signalons également que s’il existe dans le répertoire des squelettes un fichier nommé mon-chercher.php3, celui-ci sera lu à la place de
inc-chercher.php3 afin d’y trouver la définition de la fonction chercher_squelette retournant le squelette associé au document demandé.

Relations avec le serveur HTTP

Avec les améliorations ci-dessus, les squelettes interpolant du
PHP seront rares, et il convient d’essayer d’extirper
du traducteur de squelettes ses propres interpolations, afin de produire
le plus fréquemment possible des fichiers 100% HTML que le serveur HTTP pourra
renvoyer sans aucune nouvelle passe de PHP. Plusieurs situations sont à distinguer.

Passons rapidement sur le cas du surlignage :
l’implémentation standard reposait sur un deuxième passe,
qu’on a pu intégrer à la première sans difficulté, au besoin en rajoutant
des balises <span class="spip_surligne"> qui sont ignorées
lorsqu’aucun surlignage n’est demandé.

Une famille de cas plus difficiles réside dans les balises d’identification (ADMIN, LOGIN etc).
C’est en fait ici qu’un ajout à la page cachée est inévitable. L’alternative théorique
à l’interpolation de PHP dans la page cachée consiste à traduire ces
balises en un appel à une fonction JavaScript, qui sera définie dans un en-tête, recalculé
à chaque connexion, envoyé avec la page cachée. En d’autres termes, on
remplace une interpolation PHP confiée au serveur, par une interpolation JavaScript
confiée au client : ce transfert de charge est motivé par la lourdeur d’une deuxième
passe pour le serveur, alors que le client s’en acquitte sans passe supplémentaire,
cet appel à JavaScript n’étant qu’un parmi d’autres transmis pour d’autres raisons.

Cette solution séduisante théoriquement est malheureusement très délicate à
programmer. Actuellement, seule la balise la plus fréquente, FORMULAIRE_ADMIN, a été reprogrammée ainsi. Les autres, heureusement plus rares, restent à faire.

Enfin, le dernier cas concerne la balise INCLURE. L’implémentation standard interpolait un include PHP, au motif que les durées de vie des caches inclus
et de l’incluant pouvaient différer. La présente implémentation analyse le
script du squelette inclus pour en extirper le délai, et remonter cette information
aux niveaux supérieur de l’inclusion. On produit ainsi une page 100% HTML
mais respectant les délais.
Cette stratégie demande un espace disque un peu
plus important (ce qui est inévitable puisque l’on veut mémoriser plus de résultats
de calcul) et une regénération sera un plus lente pour un incluant
de durée de vie plus longue que ses inclusions (mais en pratique c’est le cas
inverse qui est le plus fréquent).

Cette stratégie a été effectivement implémentée pour les squelettes dont les
scripts d’appels sont standard (affectations des variables $fond et $delai
puis include du fichier inc_public), les autres scripts (rares) suggérant
la nécessité d’une interpolation. Cette stratégie a été rendue possible par la réécriture
du traducteur exposée plus haut, qui est totalement réentrant : n’affectant
aucune variable globale, il se permet de s’appeler récursivement pour traduire un squelette sans avoir fini de traduire le squelette incluant.
Cette stratégie demande cependant une refonte du système de cache,
qui en avait de toutes façons besoins.

Relations avec le système d’exploitation

Le système de caches standard de Spip n’est ni fiable, ni équitable, ni économe :
pour contrôler les accès simultanés de plusieurs processus à
la collection de caches disponibles dans le système de fichiers,
il pose un verrou dans le serveur SQL, lequel est inévitablement désynchronisé
avec le système de fichiers, ce qui produit des bugs certains quoique difficilement
reproductibles. L’interface avec le reste de Spip a aussi l’inconvénient de multiplier
les accès disques pour créer une page, même quand celle-ci n’est pas destinée
à être mémorisée sur le disque.

On propose ici une version reposant sur la
primitive flock du système d’exploitation, et un suivi des
dépendances des caches.

En ce qui concerne la compilation des squelettes, la méthode est simple :
on pose un verrou sur son fichier source avant de regarder si la version
compilée existe : on bloque ainsi les processus désirant
compiler la même chose, sans bloquer les processus accédant
aux caches pour d’autres raisons. Si la version compilée n’existe pas ou n’est plus
d’actualité (source plus récente que la version compilée), on la crée. A ce stade, la
version compilée existe nécessairement et on libère le verrou : un processus
éventuellement bloqué constatera alors l’existence de ce qu’il cherche au lieu
de le calculer en même temps qu’un autre (comme le fait la version actuelle).
Les squelettes sont rangés dans le sous-répertoire s du répertoire des caches,
non plus au niveau supérieur, afin d’accéder plus rapidement à chaque sous-répertoire. Leur nom est le MD5 strict du nom de leur fichier source,
inséré comme commentaire en première ligne pour les identifier facilement.

Pour les caches des pages HTML, le même principe reviendrait à dédoubler
tous les fichiers de cache par un fichier de verrouillage spécifique, ce qui
serait prohibitif. On a donc implémenté un algorithme plus compliqué.

-  le nom du fichier de cache est, au codage MD5 près, l’URL de la page demandée moins les paramètres insérés par Spip pour ses besoins propres
(on notera au passage que les squelettes référençant des variables passées dans
l’URL produiront des caches différents, ce qui est bien le but souhaité) ;

-  ce fichier se trouve dans un sous-sous-répertoire du répertoire nommé CACHE, le premier sous-répertoire étant nommé par la
seizième lettre du MD5, et le deuxième sous-répertoire étant nommé par
la durée de vie du cache (ce double niveau de hachage accélère les accès,
mais aussi le nettoyage) ;

-  lorsqu’un processus demande si un cache existe, tous les accès aux fichiers
de caches sont verrouillés (en posant un verrou en lecture sur un fichier annexe,
savoir celui contenant cette pose) et on teste
aussitôt si le cache existe ;

-  si le fichier n’existe pas, il est immédiatement créé, avec un contenu vide ;

-  à ce stade, le fichier existe nécessairement ; on demande un verrou non bloquant
sur lui ;

-  si le verrou ne peut être obtenu, c’est qu’un autre processus vient de le créer et s’occupe d’en calculer le contenu ; on libère le verrou sur le répertoire et on
demande un verrou bloquant sur le fichier, afin d’endormir le processus
jusqu’à libération du verrou par l’autre processus (ceci contraste avec la version
actuelle ou deux processus peuvent calculer simultanément le même cache) ;

-  si le verrou est obtenu, on libère le verrou sur le répertoire ;

-  à ce stade, et dans les deux cas, d’autres processus peuvent à nouveau accéder aux autres caches mais pas à celui-là (en particulier, un cache en cours de construction n’empêche pas la construction des caches inclus ce qui est indispensable à la
stratégie d’inclusion définie à la section précédente) ;

-  si le fichier de cache est vide, c’est que le processus l’ayant créé a disparu sans finir son
travail ; le processus en cours le reprend donc (situation très anomalique
du serveur : table des processus saturée etc) ;

-  la première ligne d’un fichier de cache non vide contient sa durée de vie
(éventuellement héritée de ses inclusions) ; si elle est encore valable, le verrou
est levé et le contenu du fichier est retourné ; sinon le verrou est conservé et le fichier remis à zéro ;

-  si le fichier de cache est vide, on en lance le calcul qu’on retournera en résultat, après écriture du fichier et libération de son verrou.

Lors de l’écriture du fichier de cache, le système rajoute comme première ligne
la durée de vie du cache ainsi que l’indication html ou
php selon que la page calculée nécessite ou non une deuxième
passe de PHP. Dans le cas html, le répertoire de cache est donc
un répertoire ressemblant à un banal site de pages statiques. On pourrait alors
émettre un champ Content-Length et un Connexion : close pour prévenir
plus rapidement le client que la page est achevée.

En plus de la gestion de ces accès concurrents, cette implémentation garde trace dans des tables SQL des dépendances entre caches et tables SQL standards, de sorte que lorsqu’une
brève, un article ou une rubrique sont modifiés, tous les caches y faisant référence
(directement ou par inclusion)
sont immédiatement retirés : il n’est plus nécessaire de forcer le « voir en ligne » ou
de vider explicitement tout le cache pour que le site tienne compte de ce changement. En fait, ce mode de fonctionnement existe en Spip standard pour
les seuls forums (qui n’ont qu’un seul cache associé), il est donc ici généralisé.

Toutefois, il n’est pas possible d’avoir une condition nécessaire et suffisante
(sans parler des pannes des serveurs) aussi le mécanisme de nettoyage périodique est conservé.
Il est cependant plus réactif puisque le nommage des répertoires par la durée de vie des caches qu’ils contiennent permet de supprimer les caches dès leur obsolescence, et non sur la base arbitraire d’un age supérieur à 14 jours.

Installation

Cette refonte se présente sous la forme d’une archive comprenant 17 fichiers nouveaux
et 23 nouvelles versions de fichiers existants, que voici classés par ordre
de taille décroissante de modification (taille du diff rapporté à la taille du fichier) :

ecrire/inc_base.php3
inc-calcul-squel.php3
inc-champ-squel.php3
inc-public-global.php3
inc-forum.php3
inc-calcul.php3
ecrire/inc_surligne.php3
inc-cache.php3
ecrire/controle_forum.php3
inc-admin.php3
inc-debug-squel.php3
inc-public.php3
ecrire/inc_cron.php3
spip_cache.php3
spip_image.php3
inc-login.php3
ecrire/inc.php3
inc-debug.php3
inc-stats.php3
ecrire/naviguer.php3
ecrire/breves_voir.php3
ecrire/articles.php3
inc-formulaires.php3

Il suffit de déballer cette archive dans une installation de Spip 1.7.2 pour la voir
fonctionner. Toutefois, si on veut profiter de la réactivité du système de cache
(en particulier les forums, dont on a déjà l’habitude) il faut relancer une installation
en détruisant le fichier ecrire/inc_connect.php3.

En plus de cette archive, on trouvera :

-  le fichier nouveautes.html
équivalent à nouveautes-dist.html mais sans interpolation de PHP
(le fichier inc_cron accepte néanmoins les deux versions par souci
de compatibilité).

-  le fichier tablextra.php, à installer dans ecrire/ : c’est un script qui génère automatiquement le formulaire de remplissage des entrées d’une table SQL, et qui la remplit lorsque le formulaire est retourné. On lui donne dans l’URL le nom de la table et le fichier php qui contient sa description. Ainsi :

tablextra.php?table=spip_breves&file=inc_serialbase

provoquera la construction d’un formulaire de saisie fonctionnellement proche de ecrire/breve_edit et traitera le retour du formulaire comme ecrire/breve.

Dernière modification de cette page le 31 juillet 2009

Retour en haut de la page

Répondre à cet article

Qui êtes-vous ?

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 Les choses à faire avant de poser une question (Prolégomènes aux rapports de bugs. )
Ajouter un document

Retour en haut de la page

Ça discute par ici

  • Brownie

    6 juillet 2012 – 43 commentaires

    Brownie est une adaptation pour Zpip du thème du même nom initialement développé par Egrappler.com. Présentation Brownie est un thème Responsive à deux colonnes. La démonstration ci-dessous utilise la version 2.0.0 de Brownie, la dist de SPIP3 (...)

  • Métas +

    3 décembre – 13 commentaires

    Améliorez l’indexation de vos articles dans les moteurs et leur affichage sur les réseaux sociaux grâce aux métadonnées Dublin Core, Open Graph et Twitter Card. Installation Activer le plugin dans le menu dédié. Dans le panel de configuration, (...)

  • Acces Restreint 3.0

    11 décembre 2008 – 785 commentaires

    Le plugin accès restreint permet de définir et de gérer des zones de l’espace public en accès restreint. Cette version du plugin a été redévelopée et optimisée tout spécialement pour SPIP 2.0. Il en découle une amélioration des performances sur les gros (...)

  • Compositions 2 et 3

    25 mars 2011 – 176 commentaires

    Ce plugin vous permet de définir plusieurs variantes de squelettes (nommées compositions) pour un même type d’objet SPIP. Dans l’espace privé, il est alors possible de choisir, dans un menu déroulant, la composition qu’on veut attribuer à chaque (...)

  • Configurer Sparkpost

    25 avril – 18 commentaires

    N’hésitez pas à relire le préambule de cette rubrique avant de créer un compte sur une plateforme tierce . Présentation Sparkpost est une société d’envoi de mailing https://www.sparkpost.com/ Sur les petits volumes ( <100.000 emails / mois), (...)

Ça spipe par là