SPIP

[ar] [ast] [bg] [br] [ca] [co] [cpf] [cs] [da] [de] [en] [eo] [es] [eu] [fa] [fon] [fr] [gl] [id] [it] [ja] [lb] [nl] [oc] [pl] [pt] [ro] [ru] [sk] [sv] [tr] [uk] [vi] [zh] Espace de traduction

Télécharger

Étendre SPIP

Mai 2007 — mis à jour le : 23 mars

Toutes les versions de cet article :

Si vous voulez étendre SPIP et en particulier y contribuer, les points importants à retenir sont les suivants : c’est un outil déjà utilisé par des milliers de personnes, qui a un passé et un avenir, et qui est un travail collectif. Certains choix ont été opérés, pas toujours heureux, mais qui donnent une cohérence à l’ensemble du code, facilitant son évolution. Lorsqu’un choix se révèle franchement incompatible avec une nouvelle technologie intéressante à intégrer, une réécriture est mise en chantier mais peut prendre plusieurs mois, d’où une incohérence prolongée. On comprend donc qu’il faut éviter autant que possible de telles décisions, et accepter qu’ici où là une nouvelle règle soit violée, la réécriture de cette partie du code n’ayant pas encore eu lieu. On comprend aussi que le style de programmation varie quelque peu d’un endroit à un autre, mais que se construit une identité commune en perpétuel devenir, un logiciel utile et utilisé n’étant pas de saintes écritures figées sur papier bible, mais l’expression d’une communauté vivante et libre.


Organisation des sources

Depuis la version 1.9, il est possible de modifier le comportement de SPIP sans altérer ses sources, grâce au chemin d’accès défini par la constante SPIP_PATH. Toute extension de SPIP doit opérer ainsi afin qu’un test d’intégration soit débrayable par simple redéfinition du chemin d’accès, la distribution de SPIP étant supposée en lecture seule. Si vous êtes convaincu qu’un certain comportement ne peut être obtenu ainsi, écrire à la liste de discussion spip-dev.

Les contributions à SPIP sont à décrire par un article sur Spip-Contrib et leur contenu est à déposer soit en tant que pièces jointes à cet article, soit, ce qui est mieux, sur spip-zone qui fournit un serveur Subversion piloté par Trac.

Pour étendre SPIP ainsi, il faut bien comprendre l’organisation de ces répertoires et le rôle de ses fichiers. C’est le but du présent article, mais on lira aussi avec profit l’article annonçant SPIP 1.9 qui l’a inaugurée.

La racine d’une distribution de SPIP comporte essentiellement :

-  un fichier spip.php, alias index.php, gérant la compatibilité avec les anciennes versions, chargeant le fichier d’initialisation ecrire/inc_version.php et passant immédiatement la main au script principal ecrire/public.php ;

-  un répertoire ecrire comportant exclusivement des fichiers interprétables côté serveur (PHP et SQL) ;

-  un ou plusieurs (selon les versions) répertoires comportant des fichiers interprétables côté client (HTML, Javascript, feuilles de style, images de différents formats) ainsi que les patrons de mise en page nommés squelettes. Ces squelettes sont interprétés des deux côtés : il s’agit de fichiers composés d’un format MIME complété de quelques directives SPIP, directives traitées côté serveur afin d’envoyer au client un texte purement MIME (la plupart du temps du HTML, mais aussi du RSS, du SVG, du ICS etc).

-  quatre répertoires vides à l’installation, qui contiendront les données, temporaires ou permanentes, nécessaires à la vie du site.

Rôles des répertoire prive et squelettes-dist et leurs sous-répertoires

Ils contiennent les fichiers déterminant la présentation de SPIP, pour l’espace privé et l’espace public respectivement. Avant SPIP 2.0, ces deux répertoires n’en formaient qu’un seul, nommé dist. Ils contiennent beaucoup de sous-répertoires dont voici les significations :

répertoirerôle
/ contient les squelettes. Leur nom se termine par .html pour des raisons historiques mais cela ne préjuge pas de leur contenu. Il sufit de donner un tel nom, privé de cette extension, au paramètre page de l’URL d’un site sous SPIP pour déclencher l’utilisation de ce squelette.
Ce répertoire contient également les feuilles de styles (d’extension .css) définissant l’habillage par défaut
formulaires/ contient la partie html des balises dynamiques, squelettes de formulaires dont le code PHP figure dans le répertoire ecrire/balise
icones_barre/ contient les images destinées à illustrer la barre typographique
images/ images de l’espace privé
javascript/ librairies javascript (jQuery, barre typographique, ...)
modeles/ squelettes appelables avec la balise #MODELE ou avec les raccourics <article6|modele>
polices/ polices utilisables pour les images typographiques
vignettes/ vignettes standards pour les types de documents pouvant être associés à un article

Rôles du répertoire ecrire et ses sous-répertoires

Le répertoire ecrire comporte plusieurs sous-répertoires composés de fichiers PHP définissant des fonctions et procédant éventuellement, mais rarement, à une initialisation lors de leur chargement (ces exceptions sont appelées à disparaître). Les fichiers situés au niveau du répertoire principal sont les plus importants à comprendre pour contribuer à SPIP, il s’agit de inc_version.php, public.php et index.php.

Le fichier ecrire/inc_version.php initialise les constantes et les variables globales nécessaires au fonctionnement de SPIP, notamment celles assurant sa portabilité sur les différentes plates-formes. Assez tôt lors de son chargement, il inclut le fichier inc/utils.php, où figurent les fonctions indispensables à SPIP, un fichier hors distribution nommé mes_options.php permettant de moduler cette initialisation sans avoir à modifier le fichier inc_version.php. En particulier, il est possible dans ce fichier personnel d’invoquer la fonction spip_initialisation pour définir les répertoires de données et disposer ainsi de plusieurs sites sous SPIP utilisant une seule distribution (l’appel standard de cette fonction, plus loin dans inc_version.php, sera automatiquement neutralisé). Une autre fonction indispensable à SPIP est find_in_path, exploitant le chemin d’accès, ainsi que include_spip qui repose sur find_in_path, et charger_fonction qui repose sur include_spip. Tous les fichiers de SPIP sont chargés par ces deux dernières fonctions.

Le fichier ecrire/public.php, appelé par spip.php, a pour rôle essentiel de délivrer les pages de l’espace public, commandées lorsque la requête HTTP comporte (après réécriture éventuelle) le paramètre page. Ce script applique alors le squelette ayant pour nom la valeur de ce paramètre. Il envoie les en-têtes HTTP et le contenu obtenus, gère leurs éventuelles erreurs et lance les tâches de fond à l’aide de la fonction cron. Contribuer à l’espace public de SPIP consiste donc simplement à fournir de nouveaux squelettes, avec éventuellement leurs feuilles de style et leurs images.

L’autre rôle de ecrire/public.php concerne le cas où la requête HTTP comporte l’argument action. Il applique alors la fonction charger_fonction à la valeur v de ce paramètre action. Cette application a pour effet de charger le fichier homonyme du répertoire action, dont la fonction principale action_v_dist est alors invoquée. Ces scripts effectuent essentiellement des écritures (en base ou sur fichier) et ne retournent en général pas de résultat, échappant ainsi à la problématique de la mise en page.

Le fichier index.php est le fichier central d’accès aux formulaires de l’espace privé. Il authentifie l’internaute, initialise ses données personnelles et applique la fonction charger_fonction à la valeur v du paramètre exec. Cette application a pour effet de charger le fichier homonyme du répertoire exec, dont la fonction principale exec_v_dist est alors invoquée. Celle-ci a la charge de délivrer l’intégralité du flux de sortie, y compris les en-têtes HTTP. Il est donc possible d’étendre Spip simplement en rajoutant un fichier PHP dans un sous répertoire nommé exec d’un répertoire figurant dans SPIP_PATH.

Le répertoire exec contient exclusivement les fichiers définissant les fonctions directement invocables par le paramètre d’URL exec. Le code PHP de ces fichiers ne doit jamais accéder en écriture à la base de données (les exceptions à cette règle sont en voie de disparition). A l’inverse, il y accède abondamment en lecture afin de vérifier les droits du demandeur et déterminer les données à visualiser. Si l’on veut voir SPIP sous l’archétype Modèle-Vue-Controleur, les fichiers de exec remplissent le rôle de Controleur. Si l’on veut voir SPIP sous l’archétype (Print(Eval(Read))) de Lisp, c’est la partie Read. À terme, ce répertoire devrait devenir un répertoire de squelettes. Il est demandé aux nouvelles contributions de Spip de penser à cet objectif lors de leur rédactions.

Le répertoire action, dont il a déjà été question, contient essentiellement les scripts accédant en écriture à la base de données. Si l’on veut voir SPIP sous l’archétype Modèle-Vue-Controleur, les fichiers de action remplissent le rôle de Modèle. Si l’on veut voir SPIP sous l’archétype (Print(Eval(Read))) de Lisp, c’est la partie Eval. Là encore, contribuer à SPIP consiste à écrire de tels scripts et à les invoquer par des formulaires construits avec la fonction generer_action_auteur assurant la sécurisation de l’accès à ces scripts, qui à l’inverse invoqueront la fonction securiser_action pour vérifier les droits du demandeur. Cette architecture permet de calculer ces droits seulement à la construction des formulaires appelant les scripts d’accès en écriture : plutot que de recalculer tous les droits, ces scripts vérifieront simplement que la clé figurant dans les arguments est la même que celle qu’ils recalculent à partir des autres arguments, de l’identité du demandeur et d’une valeur aléatoire renouvellée périodiquement. Ces scripts ne retournent en général pas de résultat, il n’y a donc là aussi ni code HTML, ni appel à la fonction echo (les exceptions sont appelées à disparaître). En revanche, ils sont souvent appelés avec un paramètre HTTP nommé redirect, demandant une redirection qui sera alors automatiquement opérée par public.php, qui envoie un statut HTTP 204 en l’absence de ce paramètre. Dans le cas des formulaires construits avec la fonction ajax_action_auteur, cette redirection conduit au script homonyme dans le répertoire exec. Ce deuxième script se réduit le plus souvent à charger le fichier homonyme dans le répertoire inc, d’appeler sa fonction principale dont le résultat est retourné au client par l’intermédiaire de la fonction ajax_retour. Il est ainsi très facile d’étendre SPIP en mode AJAX en utilisant cette infrastructure.

Le répertoire inc, le plus volumineux, contient essentiellement les fonctions construisant les pages de l’espace privé renvoyées au client, ces fonctions devant être à terme les filtres utilisés par les fichiers de exec quand ils seront des squelettes. Si l’on veut voir SPIP sous l’archétype Modèle-Vue-Controleur, les fichiers de inc remplissent le rôle de Vue. Si l’on veut voir SPIP sous l’archétype (Print(Eval(Read))) de Lisp, c’est la partie Print. Toutefois ce répertoire contient également beaucoup de fonctions relevant plutot du Controle et devra donc être réorganisé. La plupart des fichiers de inc sont chargés par l’intermédiaire de charger_fonction, et ce sera le cas de tous à terme. Aucune des fonctions de ce répertoire n’est censée utiliser echo. Les contributions à SPIP sont appelées à respecter ces règles dès à présent.

Le répertoire install contient exclusivement les fonctions nécessaires à l’installation de SPIP. Chaque étape peut-être surchargée ou complétée par d’autres, la fonction principale de exec/install.php utilisant ce répertoire selon le même principe que ecrire/index.php avec le répertoire exec.

Le répertoire urls contient des fichiers définissant chacun le même jeu de fonctions de réécriture d’URL. Il s’agit des fonctions calculant, à partir d’un index numérique dans une table de la base de données, un signet plus facile à lire et écrire que l’appel du script PHP effectivement opéré par le serveur HTTP pour cet index et cette table. Là encore, il suffit de rajouter un fichier dans ce répertoire pour obtenir un nouveau jeu, dont le nom sera présenté dans le panneau de configuration de l’espace privé gérant les types d’urls (avant SPIP 2.0, ce panneau n’existaient pas et ces fichiers étaient utilisés par la globale type_urls).

Le répertoire lang contient exclusivement des fichiers de données, tableaux indiquant la traduction, pour toutes les langues connues de SPIP, de tous les arguments que la fonction _T, définie dans inc/utils.php, est susceptible de recevoir. Les fichiers sont chargés exclusivement par les fonctions de inc/lang.php. Traduire les fichiers de référence *fr* en donnant un nom conventionnel aux fichiers obtenus suffit à déclarer une nouvelle langue à SPIP.

Le répertoire charset contient lui aussi exclusivement des fichiers de données, tableaux permettant de passer d’un codage de caractères à un autre (utf, iso, ascii, entités html etc). Ils sont lus exclusivement par les fonctions de inc/charsets.php. Il suffit là encore de rajouter un fichier pour disposer d’un nouveau codage, mais SPIP propose tous ceux couramment utilisés, aussi un telle intervention est rarissime.

Le répertoire base contient les fonctions d’interfaces entre PHP et les serveurs SQL que SPIP peut appeler. En particulier, le fichier générique abstract_sql.php contient les fonctions qu’il faut utiliser pour dialoguer avec les serveurs SQL, les fonctions de base de PHP pour cela ne devant pas être utilisées directement par souci de portabilité. Ne doit évidemment figurer aucun code MIME dans ce répertoire.

Le répertoire req contient les portages effectifs du serveur SQL virtuel de SPIP vers les serveurs réels (MySQL, PG) et assimilés (SQLite).

Le répertoire balise contient les fichiers PHP associés aux balises dynamiques de SPIP. Leur nom est homonyme du squelette de squelettes-dist/formulaires. Compléter l’espace public de SPIP par un formulaire F consiste à créer un fichier F.html dans son SPIP_PATH et un fichier F.php dans un sous-répertoire balise de son SPIP_PATH. Le rôle de ce fichier PHP est de réceptionner les saisies demandées par ce formulaire, et éventuellement de ré-afficher celui-ci pour complément en cas de saisies invalides. C’est sans doute le type de contribution à l’espace public la plus difficile à réaliser, car la mécanique sous-jacente exige deux passes d’exécution de PHP dont il faut bien comprendre les rôles respectifs. Avant que ce mécanisme n’existe, la stratégie de développement de formulaire consistait à écrire des squelettes comportant des instructions PHP. Il est toujours possible de le faire, mais le résultat sera peu efficace car jamais mis en cache ; et il ne dispense pas de comprendre les deux passes de PHP intrinsèques au phénomène.

Le répertoire public contient le compilateur des squelettes. C’est une partie du code assez compliquée, mais depuis SPIP 1.8 elle bénéficie d’une interface de programmation qui rend ce compilateur totalement extensible sans exiger d’en comprendre tous les détails. La description la moins incomplète est ici.

Le répertoire lib contient des sous-répertoires de bibliothèques développées en dehors de SPIP mais dont il peut avoir besoin. Actuellement, seule est fournie systématiquement la bibliothèque safehtml, fournissant des utilitaires de sécurisation des pages scriptables. Pour contribuer à cette partie, remonter directement à son site.

Dernier point : la plupart des fichiers de SPIP sont utilisés via charger_fonction qui charge un fichier et appelle sa fonction homonyme censée y être définie. Il s’ensuit que le nom d’un fichier PHP doit être composé exclusivement de caractères acceptables pour un nom de fonction PHP : on évitera donc le signe moins, le point etc.

Règles de programmation

SPIP a démarré à l’époque où PHP transformait automatiquement en variables globales les paramètres HTTP. Ce style de programmation suicidaire a été abandonné par PHP4. SPIP a suivi une évolution parallèle, mais décalée dans le temps. Bien que le code actuel ne suive donc pas toujours les règles qui vont suivre, il est demandé aux contributions à venir de les respecter dès à présent, sans attendre que SPIP cesse d’y déroger ici ou là. On pourra lire attentivement le fichier ecrire/articles.php, le plus proche des spécifications qui suivent.

-  Privilégier l’écriture par fonctions. La philosophie du logiciel libre est d’être utilisé dans un maximum de contextes différents, en conséquence le code doit être écrit dans une optique de réutilisation en dehors de son contexte initial de développement. L’écriture par fonctions ne référençant aucune variable globale et n’effectuant aucun appel à echo ou print garantit un chargement silencieux et un appel sans effets secondaires indésirables.

-  Éviter au maximum l’utilisation de variables globales. Elles sont responsables de nombreuses failles de sécurité et d’impossibilités de réutilisation du code. Les alternatives à leur usage sont :

— la constante, qui a l’avantage de signaler au lecteur que cette valeur ne changera pas pendant toute la durée du script ;

— la variable statique, qui a l’avantage de signaler au lecteur qu’il s’agit de valeur à longue durée de vie mais n’intéressant que la fonction qui la déclare.

-  Écrire du code ne produisant aucune erreur ni avertissement en mode error_reporting(E_ALL). Cela facilite la mise au point en cas de variable involontairement indéfinie. Si l’on a vraiment besoin d’exécuter du code en dehors de ce mode, utiliser l’artifice @ en le limitant au maximum à la portion de code problématique, et prévoir un message d’erreur dans le journal, en utilisant la fonction spip_log.

-  Commenter le contexte, pas le texte. Il ne sert à rien de paraphraser le nom de ses variables et fonctions, ni les fonctions PHP décrites dans son manuel : les commentaires comme boucle sur le tableau de valeurs devant un foreach ne font que grossir inutilement les fichiers. En revanche, il est souhaitable d’indiquer le type des arguments (PHP étant un langage dynamiquement typé, il se devine difficilement) et leur propriété supposée à l’entrée de la fonction (par exemple : non nul). Quand un bug difficile est corrigé ou anticipé, dire pourquoi le code initial était incorrect pour éviter qu’une réécriture ultérieure ne le réintroduise en croyant optimiser. Enfin, SPIP étant développé en français, éviter les termes absents des dictionnaires de cette langue afin de faciliter la compréhension à ceux dont ce n’est pas la langue maternelle.

-  Nommer rationnellement les fonctions et variables. L’organisation du code de SPIP est plutôt fondée sur le découpage en répertoires dédiés que sur des règles de nommage strictes, mais on évitera néanmoins les incohérences comme le multi-linguisme à l’intérieur d’un nom. Les fonctions d’un même fichier tenderont à avoir un préfixe ou un suffixe commun, inspiré du nom du fichier.

-  Tester sur un maximum de configurations. N’oubliez pas que SPIP doit fonctionner sur toute plate-forrme, tant côté client que côté serveur. Il y a nécessairement plusieurs navigateurs sur votre machine, tester votre code sur au moins deux d’entre eux. Dans la mesure du possible, essayez également plusieurs hébergeurs. Lorsque une plate-forme force une écriture étrange, la mentionner explicitement, en précisant sa version et la date de l’essai.

Règles de codage

SPIP est soucieux de la qualité typographique des pages qu’il envoie aux clients HTTP, il est donc lui-même soucieux de celle de ses propres fichiers.

Les règles de codage suivent la norme PSR-2 avec quelques exceptions et ajouts listés ci-dessous :

  • Les variables, les fonctions et leurs arguments devraient être déclarées en snake_case (réf. PSR-1 - Vue d’ensemble).
    1. function ma_super_fonction($un_argument, $autre_argument) {
    2.         …
    3. }
  • Utilisez le caractère de tabulation pour indenter, car il permet à chacun⋅e de choisir librement la profondeur d’indentation dans les options de son éditeur de textes. Les tabulations doivent être utilisées en début de ligne pour l’indentation, les espaces uniquement pour l’alignement de contenu (réf. PSR-2 - Indentation).
  • L’accolade ouvrante doit être sur la même ligne que le nom de la méthode, comme pour les structures de contrôle. Et doit avoir un espace avant et aucun espace après (réf. PSR-2 - Méthodes).
    1. class MaClasse {
    2.         function une_methode() {
    3.                 …
    4.         }
    5. }
  • Les constantes doivent être déclarées en majuscules avec un tiret bas séparateur et préfixées par un tiret bas (réf. PSR-1 - Les constantes).
    1. define('_NOUVELLE_CONSTANTE', true);
  • Les variables globales, dont l’usage est déconseillé, sont référencée par $GLOBALS["xxx"] et non par une déclaration global $xxx, car cela permet d’être certain de la référence qu’on utilise et de facilement faire la différence lors de la lecture du code.
    1. if (isset($GLOBALS['meta']['adresse_site'])) {
    2.         $adresse = $GLOBALS['meta']['adresse_site'];
    3. }
  • Dans les expressions PHP laisser un espace de part et d’autre des opérateurs binaires (+, =, *, AND, ...).
    1. $hypothenuse = sqrt(($a * $a) + ($b * $b));
  • Les opérateurs unaires (!, ...) doivent être collés au paramètre auquel ils s’appliquent.
  • Utilisez les guillemets doubles uniquement quand cela est nécessaire. Si vous n’avez pas de variable ($var) ou de caractère de contrôle (\n, \r, \t...) à évaluer dans la chaîne, utilisez les guillemets simples.
    1. $chaine = "<a href=\"$url\">Lien</a>\n";
    2. $chaine2 = 'Une simple phrase : ' . $autre_chose

Voir le squelette de cette page Site réalisé avec SPIP | Espace de traduction | Espace privé