From 7978f4236a38538cdc6879c7eebf090a295178d8 Mon Sep 17 00:00:00 2001
From: JamesRezo <james@rezo.net>
Date: Sat, 12 Feb 2022 19:44:30 +0100
Subject: [PATCH] =?UTF-8?q?refactor:=20D=C3=A9placer=20les=20classes=20de?=
 =?UTF-8?q?=20ecrire/public=20dans=20Spip\Core?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ecrire/action/editer_article.php     |   2 +-
 ecrire/action/editer_auteur.php      |   2 +-
 ecrire/action/editer_rubrique.php    |   4 +-
 ecrire/balise/formulaire_.php        |   2 +-
 ecrire/base/abstract_sql.php         |  71 ++-
 ecrire/base/connect_sql.php          |   2 +-
 ecrire/base/objets.php               |   2 +
 ecrire/inc/acces.php                 |   2 +-
 ecrire/inc/admin.php                 |   2 +-
 ecrire/inc/bandeau.php               |   4 +-
 ecrire/inc/charsets.php              |  54 +-
 ecrire/inc/filtres.php               | 124 ++---
 ecrire/inc/filtres_dates.php         |   4 +-
 ecrire/inc/filtres_mime.php          |   2 +-
 ecrire/inc/filtres_mini.php          |  10 +-
 ecrire/inc/install.php               |   4 +-
 ecrire/inc/lister_objets.php         |   2 +-
 ecrire/inc/modifier.php              |   2 +-
 ecrire/inc/presentation.php          |   4 +-
 ecrire/inc/presentation_mini.php     |   4 +-
 ecrire/inc/rubriques.php             |   2 +
 ecrire/inc/texte.php                 |  16 +-
 ecrire/inc/texte_mini.php            |  12 +-
 ecrire/inc/urls.php                  |   2 +-
 ecrire/inc/utils.php                 |  12 +-
 ecrire/inc/xml.php                   |   2 +-
 ecrire/inc_version.php               |   2 +-
 ecrire/iterateur/condition.php       |   2 +
 ecrire/iterateur/data.php            |   2 +
 ecrire/iterateur/php.php             |   2 +
 ecrire/iterateur/pour.php            |   2 +
 ecrire/public/assembler.php          |   2 +-
 ecrire/public/balises.php            |  10 +-
 ecrire/public/compiler.php           |   5 +
 ecrire/public/composer.php           |   2 +-
 ecrire/public/criteres.php           |  12 +-
 ecrire/public/debusquer.php          |   2 +
 ecrire/public/decompiler.php         |   2 +
 ecrire/public/fonctions.php          |   6 +-
 ecrire/public/interfaces.php         | 771 ---------------------------
 ecrire/public/jointures.php          |   2 +
 ecrire/public/normaliser.php         |   2 +
 ecrire/public/phraser_html.php       |   8 +
 ecrire/public/references.php         |   7 +-
 ecrire/public/sandbox.php            |   4 +-
 ecrire/req/mysql.php                 |  60 +--
 ecrire/req/sqlite_generique.php      |  66 +--
 ecrire/src/Core/Balise.php           | 108 ++++
 ecrire/src/Core/Boucle.php           | 373 +++++++++++++
 ecrire/src/Core/Champ.php            |  14 +
 ecrire/src/Core/Contexte.php         |  46 ++
 ecrire/src/Core/Critere.php          |  39 ++
 ecrire/src/Core/Idiome.php           |  91 ++++
 ecrire/src/Core/Inclure.php          |  69 +++
 ecrire/src/Core/Polyglotte.php       |  24 +
 ecrire/src/Core/Tests/BaliseTest.php |   8 +
 ecrire/src/Core/Texte.php            |  36 ++
 57 files changed, 1103 insertions(+), 1025 deletions(-)
 create mode 100644 ecrire/src/Core/Balise.php
 create mode 100644 ecrire/src/Core/Boucle.php
 create mode 100644 ecrire/src/Core/Champ.php
 create mode 100644 ecrire/src/Core/Contexte.php
 create mode 100644 ecrire/src/Core/Critere.php
 create mode 100644 ecrire/src/Core/Idiome.php
 create mode 100644 ecrire/src/Core/Inclure.php
 create mode 100644 ecrire/src/Core/Polyglotte.php
 create mode 100644 ecrire/src/Core/Tests/BaliseTest.php
 create mode 100644 ecrire/src/Core/Texte.php

diff --git a/ecrire/action/editer_article.php b/ecrire/action/editer_article.php
index d2b8e086fd..510a82eabb 100644
--- a/ecrire/action/editer_article.php
+++ b/ecrire/action/editer_article.php
@@ -36,7 +36,7 @@ if (!defined('_ECRIRE_INC_VERSION')) {
  *     Identifiant de l'article. En absence utilise l'argument
  *     de l'action sécurisée.
  * @return array
- *     Liste (identifiant de l'article, Texte d'erreur éventuel)
+ *     Liste (identifiant de l'article, texte d'erreur éventuel)
  */
 function action_editer_article_dist($arg = null) {
 	include_spip('inc/autoriser');
diff --git a/ecrire/action/editer_auteur.php b/ecrire/action/editer_auteur.php
index 8b6983eaad..712ff15b17 100644
--- a/ecrire/action/editer_auteur.php
+++ b/ecrire/action/editer_auteur.php
@@ -33,7 +33,7 @@ if (!defined('_ECRIRE_INC_VERSION')) {
  *     Identifiant de l'auteur. En absence utilise l'argument
  *     de l'action sécurisée.
  * @return array
- *     Liste (identifiant de l'auteur, Texte d'erreur éventuel)
+ *     Liste (identifiant de l'auteur, texte d'erreur éventuel)
  */
 function action_editer_auteur_dist($arg = null) {
 
diff --git a/ecrire/action/editer_rubrique.php b/ecrire/action/editer_rubrique.php
index 3d08f717bb..2718f33d5b 100644
--- a/ecrire/action/editer_rubrique.php
+++ b/ecrire/action/editer_rubrique.php
@@ -128,7 +128,7 @@ function rubrique_inserer($id_parent, $set = null) {
  * @return bool|string
  *     - false  : Aucune modification, aucun champ n'est à modifier
  *     - chaîne vide : Vide si tout s'est bien passé
- *     - chaîne : Texte d'un message d'erreur
+ *     - chaîne : texte d'un message d'erreur
  */
 function rubrique_modifier($id_rubrique, $set = null) {
 	include_spip('inc/autoriser');
@@ -230,7 +230,7 @@ function editer_rubrique_breves($id_rubrique, $id_parent, $c = []) {
  * @global array $GLOBALS ['visiteur_session']
  * @return string
  *     Chaîne vide : aucune erreur
- *     Chaîne : Texte du message d'erreur
+ *     Chaîne : texte du message d'erreur
  */
 function rubrique_instituer($id_rubrique, $c) {
 	// traitement de la rubrique parente
diff --git a/ecrire/balise/formulaire_.php b/ecrire/balise/formulaire_.php
index e98025a284..8d06980b24 100644
--- a/ecrire/balise/formulaire_.php
+++ b/ecrire/balise/formulaire_.php
@@ -175,7 +175,7 @@ function balise_FORMULAIRE__dyn($form, ...$args) {
  * @param array $args
  *     Arguments envoyés à l'appel du formulaire
  * @return array|string
- *     array: Contexte d'environnement à envoyer au squelette
+ *     array: contexte d'environnement à envoyer au squelette
  *     string: Formulaire non applicable (message d’explication)
  **/
 function balise_FORMULAIRE__contexte($form, $args) {
diff --git a/ecrire/base/abstract_sql.php b/ecrire/base/abstract_sql.php
index 62a25d1d4f..c9af3639e1 100644
--- a/ecrire/base/abstract_sql.php
+++ b/ecrire/base/abstract_sql.php
@@ -199,11 +199,11 @@ function sql_set_charset($charset, $serveur = '', $option = true) {
  * @param array|string $where
  *     Conditions a remplir (Where)
  * @param array|string $groupby
- *     Critere de regroupement (Group by)
+ *     critere de regroupement (Group by)
  * @param array|string $orderby
  *     Tableau de classement (Order By)
  * @param string $limit
- *     Critere de limite (Limit)
+ *     critere de limite (Limit)
  * @param string|array $having
  *     Tableau ou chaine des des post-conditions à remplir (Having)
  * @param string $serveur
@@ -304,11 +304,11 @@ function sql_select(
  * @param array|string $where
  *    Conditions a remplir (Where)
  * @param array|string $groupby
- *    Critere de regroupement (Group by)
+ *    critere de regroupement (Group by)
  * @param array|string $orderby
  *    Tableau de classement (Order By)
  * @param string $limit
- *    Critere de limite (Limit)
+ *    critere de limite (Limit)
  * @param string|array $having
  *     Tableau ou chaine des des post-conditions à remplir (Having)
  * @param string $serveur
@@ -355,7 +355,7 @@ function sql_get_select(
  * @param array|string $where
  *    Conditions a remplir (Where)
  * @param array|string $groupby
- *    Critere de regroupement (Group by)
+ *    critere de regroupement (Group by)
  * @param string|array $having
  *     Tableau ou chaine des des post-conditions à remplir (Having)
  * @param string $serveur
@@ -696,7 +696,7 @@ function sql_free($res, $serveur = '', $option = true) {
  *
  * @return bool|string
  *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
- *     - Texte de la requête si demandé,
+ *     - texte de la requête si demandé,
  *     - False en cas d'erreur.
  **/
 function sql_insert($table, $noms, $valeurs, $desc = [], $serveur = '', $option = true) {
@@ -747,7 +747,7 @@ function sql_insert($table, $noms, $valeurs, $desc = [], $serveur = '', $option
  *
  * @return int|bool|string
  *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
- *     - Texte de la requête si demandé,
+ *     - texte de la requête si demandé,
  *     - False en cas d'erreur.
  **/
 function sql_insertq($table, $couples = [], $desc = [], $serveur = '', $option = true) {
@@ -791,9 +791,9 @@ function sql_insertq($table, $couples = [], $desc = [], $serveur = '', $option =
  *     - 'continue' : ne pas échouer en cas de serveur sql indisponible
  *
  * @return bool|string
- *     - True en cas de succès,
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - true en cas de succès,
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function sql_insertq_multi($table, $couples = [], $desc = [], $serveur = '', $option = true) {
 	$f = sql_serveur('insertq_multi', $serveur, $option === 'continue' or $option === false);
@@ -897,8 +897,8 @@ function sql_update($table, $exp, $where = '', $desc = [], $serveur = '', $optio
  *     - 'continue' : ne pas échouer en cas de serveur sql indisponible
  * @return bool|string
  *     - true si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function sql_updateq($table, $exp, $where = '', $desc = [], $serveur = '', $option = true) {
 	$f = sql_serveur('updateq', $serveur, $option === 'continue' or $option === false);
@@ -937,8 +937,8 @@ function sql_updateq($table, $exp, $where = '', $desc = [], $serveur = '', $opti
  *
  * @return bool|string
  *     - int : nombre de suppressions réalisées,
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function sql_delete($table, $where = '', $serveur = '', $option = true) {
 	$f = sql_serveur('delete', $serveur, $option === 'continue' or $option === false);
@@ -982,8 +982,8 @@ function sql_delete($table, $where = '', $serveur = '', $option = true) {
  *
  * @return bool|string
  *     - true si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function sql_replace($table, $couples, $desc = [], $serveur = '', $option = true) {
 	$f = sql_serveur('replace', $serveur, $option === 'continue' or $option === false);
@@ -1029,8 +1029,8 @@ function sql_replace($table, $couples, $desc = [], $serveur = '', $option = true
  *
  * @return bool|string
  *     - true si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function sql_replace_multi($table, $tab_couples, $desc = [], $serveur = '', $option = true) {
 	$f = sql_serveur('replace_multi', $serveur, $option === 'continue' or $option === false);
@@ -1065,9 +1065,9 @@ function sql_replace_multi($table, $tab_couples, $desc = [], $serveur = '', $opt
  *     - true : exécuter la requête
  *     - 'continue' : ne pas échouer en cas de serveur sql indisponible
  * @return bool|string
- *     - True en cas de succès,
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - true en cas de succès,
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function sql_drop_table($table, $exist = '', $serveur = '', $option = true) {
 	$f = sql_serveur('drop_table', $serveur, $option === 'continue' or $option === false);
@@ -1099,7 +1099,7 @@ function sql_drop_table($table, $exist = '', $serveur = '', $option = true) {
  *     - true : exécuter la requête
  *     - 'continue' : ne pas échouer en cas de serveur sql indisponible
  * @return bool|string
- *     - string Texte de la requête si demandé
+ *     - string texte de la requête si demandé
  *     - true si la requête a réussie, false sinon
  */
 function sql_drop_view($table, $exist = '', $serveur = '', $option = true) {
@@ -1257,10 +1257,9 @@ function sql_showtable($table, $table_spip = false, $serveur = '', $option = tru
  *     - true : exécuter la requête
  *     - 'continue' : ne pas échouer en cas de serveur sql indisponible
  * @return bool|string
- *     - True si la table existe,
- *     - False sinon,
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - true si la table existe,
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function sql_table_exists(string $table, bool $table_spip = true, $serveur = '', $option = true) {
 	$f = sql_serveur('table_exists', $serveur, $option === 'continue' or $option === false);
@@ -1432,7 +1431,7 @@ function sql_create_view($nom, $select_query, $serveur = '', $option = true) {
  *     - true : exécuter la requête
  *     - 'continue' : ne pas échouer en cas de serveur sql indisponible
  * @return string
- *     Texte de sélection pour la requête
+ *     texte de sélection pour la requête
  */
 function sql_multi($sel, $lang, $serveur = '', $option = true) {
 	$f = sql_serveur('multi', $serveur, $option === 'continue' or $option === false);
@@ -1486,7 +1485,7 @@ function sql_errno($serveur = '') {
  * Retourne une explication de requête (Explain) SQL
  *
  * @api
- * @param string $q Texte de la requête
+ * @param string $q texte de la requête
  * @param string $serveur Nom de la connexion
  * @param bool|string $option
  *     Peut avoir 3 valeurs :
@@ -1549,7 +1548,7 @@ function sql_optimize($table, $serveur = '', $option = true) {
  *     - true : exécuter la requête
  *     - 'continue' : ne pas échouer en cas de serveur sql indisponible
  * @return bool|string
- *     - string Texte de la requête si demandée,
+ *     - string texte de la requête si demandée,
  *     - true si la requête a réussie, false sinon
  */
 function sql_repair($table, $serveur = '', $option = true) {
@@ -1582,7 +1581,7 @@ function sql_repair($table, $serveur = '', $option = true) {
  *     - true : exécuter la requête
  *     - 'continue' : ne pas échouer en cas de serveur sql indisponible
  * @return array|resource|string|bool
- *     - string : Texte de la requête si on ne l'exécute pas
+ *     - string : texte de la requête si on ne l'exécute pas
  *     - ressource|bool : Si requête exécutée
  *     - array : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
  */
@@ -1623,11 +1622,11 @@ function sql_query($ins, $serveur = '', $option = true) {
  * @param array|string $where
  *    Conditions a remplir (Where)
  * @param array|string $groupby
- *    Critere de regroupement (Group by)
+ *    critere de regroupement (Group by)
  * @param array|string $orderby
  *    Tableau de classement (Order By)
  * @param string $limit
- *    Critere de limite (Limit)
+ *    critere de limite (Limit)
  * @param string|array $having
  *     Tableau ou chaine des des post-conditions à remplir (Having)
  * @param string $serveur
@@ -1691,11 +1690,11 @@ function sql_fetsel(
  * @param array|string $where
  *    Conditions a remplir (Where)
  * @param array|string $groupby
- *    Critere de regroupement (Group by)
+ *    critere de regroupement (Group by)
  * @param array|string $orderby
  *    Tableau de classement (Order By)
  * @param string $limit
- *    Critere de limite (Limit)
+ *    critere de limite (Limit)
  * @param string|array $having
  *     Tableau ou chaine des des post-conditions à remplir (Having)
  * @param string $serveur
@@ -2147,11 +2146,11 @@ function sql_in($champ, $valeurs, $not = '', $serveur = '', $option = true) {
  * @param array|string $where
  *     Conditions a remplir (Where)
  * @param array|string $groupby
- *     Critere de regroupement (Group by)
+ *     critere de regroupement (Group by)
  * @param array|string $orderby
  *     Tableau de classement (Order By)
  * @param string $limit
- *     Critere de limite (Limit)
+ *     critere de limite (Limit)
  * @param string|array $having
  *     Tableau ou chaine des des post-conditions à remplir (Having)
  * @param string $serveur
diff --git a/ecrire/base/connect_sql.php b/ecrire/base/connect_sql.php
index bb1cec3c82..e57e97457d 100644
--- a/ecrire/base/connect_sql.php
+++ b/ecrire/base/connect_sql.php
@@ -511,7 +511,7 @@ function query_reinjecte_textes($query, $textes) {
  * @deprecated 3.1 Pour compatibilité.
  * @see sql_query() ou l'API `sql_*`.
  *
- * @param string $query Texte de la requête
+ * @param string $query texte de la requête
  * @param string $serveur Nom du connecteur pour la base de données
  * @return bool|mixed
  *     - false si on ne peut pas exécuter la requête
diff --git a/ecrire/base/objets.php b/ecrire/base/objets.php
index 3be6dfd8bb..c5bdffcd70 100644
--- a/ecrire/base/objets.php
+++ b/ecrire/base/objets.php
@@ -10,6 +10,8 @@
  *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+use Spip\Core\Boucle;
+
 /**
  * Fonctions relatives aux objets éditoriaux et SQL
  *
diff --git a/ecrire/inc/acces.php b/ecrire/inc/acces.php
index 54166c9493..767c67d1c8 100644
--- a/ecrire/inc/acces.php
+++ b/ecrire/inc/acces.php
@@ -236,7 +236,7 @@ function generer_url_api_low_sec(string $script, string $format, string $fond, s
 
 
 /**
- * Inclure les arguments significatifs pour le hachage
+ * inclure les arguments significatifs pour le hachage
  *
  * Cas particulier du statut pour compatibilité ancien rss/suivi_revisions
  *
diff --git a/ecrire/inc/admin.php b/ecrire/inc/admin.php
index 53b70b8e0e..63d4aeed44 100644
--- a/ecrire/inc/admin.php
+++ b/ecrire/inc/admin.php
@@ -284,7 +284,7 @@ function fin_admin($action) {
  * @param string $suite
  *     Corps du formulaire
  * @param string $submit
- *     Texte du bouton de validation
+ *     texte du bouton de validation
  * @return string
  *     Code HTML du formulaire
  **/
diff --git a/ecrire/inc/bandeau.php b/ecrire/inc/bandeau.php
index b401b240c5..d20f727d1e 100644
--- a/ecrire/inc/bandeau.php
+++ b/ecrire/inc/bandeau.php
@@ -31,10 +31,10 @@ include_spip('inc/boutons');
  * l'ajoute au contexte.
  *
  * @param null|array $contexte
- *     Contexte connu.
+ *     contexte connu.
  *     S'il n'est pas transmis, on prend `$_GET`
  * @return array
- *     Contexte
+ *     contexte
  **/
 function definir_barre_contexte($contexte = null) {
 	if (is_null($contexte)) {
diff --git a/ecrire/inc/charsets.php b/ecrire/inc/charsets.php
index 5ceb38298f..876e034057 100644
--- a/ecrire/inc/charsets.php
+++ b/ecrire/inc/charsets.php
@@ -168,7 +168,7 @@ function test_iconv(): bool {
  * @param string $charset_cible
  *     Charset de destination (unicode par défaut)
  * @return string|array
- *     Texte corrigé
+ *     texte corrigé
  **/
 function corriger_caracteres_windows($texte, $charset = 'AUTO', $charset_cible = 'unicode') {
 	static $trans;
@@ -249,11 +249,11 @@ function corriger_caracteres_windows($texte, $charset = 'AUTO', $charset_cible =
  * Transforme les &eacute; en &#123;
  *
  * @param string $texte
- *     Texte à convertir
+ *     texte à convertir
  * @param bool $secure
  *     true pour *ne pas convertir* les caracteres malins &lt; &amp; etc.
  * @return string
- *     Texte converti
+ *     texte converti
  **/
 function html2unicode($texte, $secure = false) {
 	if (strpos($texte, '&') === false) {
@@ -285,9 +285,9 @@ function html2unicode($texte, $secure = false) {
  * Transforme &angle; en &#x2220; ainsi que toutes autres entités mathématiques
  *
  * @param string $texte
- *     Texte à convertir
+ *     texte à convertir
  * @return string
- *     Texte converti
+ *     texte converti
  **/
 function mathml2unicode($texte) {
 	static $trans;
@@ -313,12 +313,12 @@ function mathml2unicode($texte) {
  *     convertir les accents iso-8859-1
  *
  * @param string $texte
- *     Texte à convertir
+ *     texte à convertir
  * @param string $charset
  *     Charset actuel du texte
  *     Par défaut (AUTO), le charset est celui du site.
  * @return string
- *     Texte converti en unicode
+ *     texte converti en unicode
  **/
 function charset2unicode($texte, $charset = 'AUTO' /* $forcer: obsolete*/) {
 	static $trans;
@@ -400,12 +400,12 @@ function charset2unicode($texte, $charset = 'AUTO' /* $forcer: obsolete*/) {
  * ont ete encodees ainsi c'est a dessein
  *
  * @param string $texte
- *     Texte unicode à transformer
+ *     texte unicode à transformer
  * @param string $charset
  *     Charset à appliquer au texte
  *     Par défaut (AUTO), le charset sera celui du site.
  * @return string
- *     Texte transformé dans le charset souhaité
+ *     texte transformé dans le charset souhaité
  **/
 function unicode2charset($texte, $charset = 'AUTO') {
 	static $CHARSET_REVERSE = [];
@@ -454,12 +454,12 @@ function unicode2charset($texte, $charset = 'AUTO') {
  * Les caractères non resolus sont transformés en `&#123`;
  *
  * @param string $texte
- *     Texte unicode à importer
+ *     texte unicode à importer
  * @param string $charset
  *     Charset d'origine du texte
  *     Par défaut (AUTO), le charset d'origine est celui du site.
  * @return string
- *     Texte transformé dans le charset site
+ *     texte transformé dans le charset site
  **/
 function importer_charset($texte, $charset = 'AUTO') {
 	$s = null;
@@ -505,9 +505,9 @@ function importer_charset($texte, $charset = 'AUTO') {
  * Utilise la librairie mb si présente
  *
  * @param string $source
- *    Texte UTF-8 à transformer
+ *    texte UTF-8 à transformer
  * @return string
- *    Texte transformé en unicode
+ *    texte transformé en unicode
  **/
 function utf_8_to_unicode($source) {
 
@@ -609,9 +609,9 @@ function utf_8_to_unicode($source) {
  * => tout ca sera osolete quand on sera surs d'avoir mb_string
  *
  * @param string $source
- *    Texte UTF-8 à transformer
+ *    texte UTF-8 à transformer
  * @return string
- *    Texte transformé en unicode
+ *    texte transformé en unicode
  **/
 function utf_32_to_unicode($source) {
 
@@ -678,9 +678,9 @@ function caractere_utf_8($num) {
  * Convertit un texte unicode en utf-8
  *
  * @param string $texte
- *     Texte à convertir
+ *     texte à convertir
  * @return string
- *     Texte converti
+ *     texte converti
  **/
 function unicode_to_utf_8($texte) {
 
@@ -726,9 +726,9 @@ function unicode_to_utf_8($texte) {
  * Convertit les unicode &#264; en javascript \u0108
  *
  * @param string $texte
- *     Texte à convertir
+ *     texte à convertir
  * @return string
- *     Texte converti
+ *     texte converti
  **/
 function unicode_to_javascript($texte) {
 	$vu = [];
@@ -746,9 +746,9 @@ function unicode_to_javascript($texte) {
  * Convertit les %uxxxx (envoyés par javascript) en &#yyy unicode
  *
  * @param string $texte
- *     Texte à convertir
+ *     texte à convertir
  * @return string
- *     Texte converti
+ *     texte converti
  **/
 function javascript_to_unicode($texte) {
 	while (preg_match(',%u([0-9A-F][0-9A-F][0-9A-F][0-9A-F]),', $texte, $regs)) {
@@ -762,9 +762,9 @@ function javascript_to_unicode($texte) {
  * Convertit les %E9 (envoyés par le browser) en chaîne du charset du site (binaire)
  *
  * @param string $texte
- *     Texte à convertir
+ *     texte à convertir
  * @return string
- *     Texte converti
+ *     texte converti
  **/
 function javascript_to_binary($texte) {
 	while (preg_match(',%([0-9A-F][0-9A-F]),', $texte, $regs)) {
@@ -883,7 +883,7 @@ function translitteration_chiffree($car): string {
  * Reconnaitre le BOM utf-8 (0xEFBBBF)
  *
  * @param string $texte
- *    Texte dont on vérifie la présence du BOM
+ *    texte dont on vérifie la présence du BOM
  * @return bool
  *    true s'il a un BOM
  **/
@@ -900,7 +900,7 @@ function bom_utf8($texte): bool {
  * @link http://w3.org/International/questions/qa-forms-utf-8.html
  *
  * @param string $string
- *     Texte dont on vérifie qu'il est de l'utf-8
+ *     texte dont on vérifie qu'il est de l'utf-8
  * @return bool
  *     true si c'est le cas
  **/
@@ -926,7 +926,7 @@ function is_utf8($string): bool {
  * Vérifie qu'une chaîne est en ascii valide
  *
  * @param string $string
- *     Texte dont on vérifie qu'il est de l'ascii
+ *     texte dont on vérifie qu'il est de l'ascii
  * @return bool
  *     true si c'est le cas
  **/
@@ -952,7 +952,7 @@ function is_ascii($string): bool {
  * @param string $headers
  *     Éventuels headers HTTP liés à cette page
  * @return string
- *     Texte transcodé dans le charset du site
+ *     texte transcodé dans le charset du site
  **/
 function transcoder_page($texte, $headers = ''): string {
 
diff --git a/ecrire/inc/filtres.php b/ecrire/inc/filtres.php
index 197adbd93f..422f73fffb 100644
--- a/ecrire/inc/filtres.php
+++ b/ecrire/inc/filtres.php
@@ -49,8 +49,8 @@ function charger_filtre($fonc, $default = 'filtre_identite_dist') {
 /**
  * Retourne le texte tel quel
  *
- * @param string $texte Texte
- * @return string Texte
+ * @param string $texte texte
+ * @return string texte
  **/
 function filtre_identite_dist($texte) {
  return $texte;
@@ -139,11 +139,11 @@ function chercher_filtre($fonc, $default = null) {
  * @uses appliquer_filtre_sinon()
  *
  * @param mixed $arg
- *     Texte (le plus souvent) sur lequel appliquer le filtre
+ *     texte (le plus souvent) sur lequel appliquer le filtre
  * @param string $filtre
  *     Nom du filtre à appliquer
  * @return string
- *     Texte traité par le filtre si le filtre existe,
+ *     texte traité par le filtre si le filtre existe,
  *     Chaîne vide sinon.
  **/
 function appliquer_filtre($arg, $filtre) {
@@ -165,12 +165,12 @@ function appliquer_filtre($arg, $filtre) {
  * @uses appliquer_filtre_sinon()
  *
  * @param mixed $arg
- *     Texte (le plus souvent) sur lequel appliquer le filtre
+ *     texte (le plus souvent) sur lequel appliquer le filtre
  * @param string $filtre
  *     Nom du filtre à appliquer
  * @return string
- *     Texte traité par le filtre si le filtre existe,
- *     Texte d'origine sinon
+ *     texte traité par le filtre si le filtre existe,
+ *     texte d'origine sinon
  **/
 function appliquer_si_filtre($arg, $filtre) {
 	$args = func_get_args();
@@ -502,7 +502,7 @@ function filtre_debug($val, $key = null) {
  *     - le second est le texte sur lequel on applique le filtre
  *     - les suivants sont les arguments du filtre image souhaité.
  * @return string
- *     Texte qui a reçu les filtres
+ *     texte qui a reçu les filtres
  **/
 function image_filtrer($args) {
 	$filtre = array_shift($args); # enlever $filtre
@@ -861,9 +861,9 @@ function entites_html($texte, $tout = false, $quote = true) {
  * @link https://www.spip.net/5513
  *
  * @param string $texte
- *     Texte à convertir
+ *     texte à convertir
  * @return string
- *     Texte converti
+ *     texte converti
  **/
 function filtrer_entites(?string $texte): string {
 	$texte ??= '';
@@ -950,9 +950,9 @@ function corriger_caracteres($texte) {
  * @link https://www.spip.net/4287
  *
  * @param string $texte
- *     Texte à transformer
+ *     texte à transformer
  * @return string
- *     Texte encodé pour XML
+ *     texte encodé pour XML
  */
 function texte_backend(string $texte): string {
 	if ($texte === '') {
@@ -1004,9 +1004,9 @@ function texte_backend(string $texte): string {
  * @filtre
  *
  * @param string $texte
- *     Texte à transformer
+ *     texte à transformer
  * @return string
- *     Texte encodé et quote pour XML
+ *     texte encodé et quote pour XML
  */
 function texte_backendq(string $texte): string {
 	return addslashes(texte_backend($texte));
@@ -1092,11 +1092,11 @@ function recuperer_numero(?string $texte): string {
  *     Ce filtre supprime aussi les signes inférieurs `<` rencontrés.
  *
  * @param string|array|null $texte
- *     Texte ou tableau de textes à échapper
+ *     texte ou tableau de textes à échapper
  * @param string $rempl
  *     Inutilisé.
  * @return string|array
- *     Texte ou tableau de textes converti
+ *     texte ou tableau de textes converti
  **/
 function supprimer_tags($texte, $rempl = '') {
 	if ($texte === null) {
@@ -1124,11 +1124,11 @@ function supprimer_tags($texte, $rempl = '') {
  *     ```
  *
  * @param string $texte
- *     Texte à échapper
+ *     texte à échapper
  * @param string $rempl
  *     Inutilisé.
  * @return string
- *     Texte converti
+ *     texte converti
  **/
 function echapper_tags($texte, $rempl = '') {
 	$texte = preg_replace('/<([^>]*)>/', "&lt;\\1&gt;", $texte);
@@ -1149,9 +1149,9 @@ function echapper_tags($texte, $rempl = '') {
  *     ```
  *
  * @param string $texte
- *     Texte à convertir
+ *     texte à convertir
  * @return string
- *     Texte converti
+ *     texte converti
  **/
 function textebrut($texte) {
 	$u = $GLOBALS['meta']['pcre_u'];
@@ -1176,9 +1176,9 @@ function textebrut($texte) {
  * @link https://www.spip.net/4297
  *
  * @param string $texte
- *     Texte avec des liens
+ *     texte avec des liens
  * @return string
- *     Texte avec liens ouvrants
+ *     texte avec liens ouvrants
  **/
 function liens_ouvrants($texte) {
 	if (
@@ -1236,9 +1236,9 @@ function liens_nofollow($texte) {
  *     ```
  *
  * @param string $texte
- *     Texte à transformer
+ *     texte à transformer
  * @return string
- *     Texte sans paraghaphes
+ *     texte sans paraghaphes
  **/
 function PtoBR($texte) {
 	$u = $GLOBALS['meta']['pcre_u'];
@@ -1267,8 +1267,8 @@ function PtoBR($texte) {
  * @deprecated 3.1
  * @see Utiliser le style CSS `word-wrap:break-word;`
  *
- * @param string $texte Texte
- * @return string Texte encadré du style CSS
+ * @param string $texte texte
+ * @return string texte encadré du style CSS
  */
 function lignes_longues($texte) {
 	if (!strlen(trim($texte))) {
@@ -1293,8 +1293,8 @@ function lignes_longues($texte) {
  *     [(#EXTENSION|majuscules)]
  *     ```
  *
- * @param string $texte Texte
- * @return string Texte en majuscule
+ * @param string $texte texte
+ * @return string texte en majuscule
  */
 function majuscules($texte) {
 	if (!strlen($texte)) {
@@ -1375,11 +1375,11 @@ function taille_en_octets($taille) {
  * @uses texte_backend()
  *
  * @param ?string $texte
- *     Texte à mettre en attribut
+ *     texte à mettre en attribut
  * @param bool $textebrut
  *     Passe le texte en texte brut (enlève les balises html) ?
  * @return string
- *     Texte prêt pour être utilisé en attribut HTML
+ *     texte prêt pour être utilisé en attribut HTML
  **/
 function attribut_html(?string $texte, $textebrut = true): string {
 	if ($texte === null) {
@@ -1914,15 +1914,15 @@ function alterner($i, ...$args) {
  * (dans ce cas l'option `$complet` n'est pas disponible)
  *
  * @param string|array $balise
- *     Texte ou liste de textes dont on veut extraire des balises
+ *     texte ou liste de textes dont on veut extraire des balises
  * @param string $attribut
  *     Nom de l'attribut désiré
  * @param bool $complet
- *     True pour retourner un tableau avec
+ *     true pour retourner un tableau avec
  *     - le texte de la balise
  *     - l'ensemble des résultats de la regexp ($r)
  * @return string|array|null
- *     - Texte de l'attribut retourné, ou tableau des textes d'attributs
+ *     - texte de l'attribut retourné, ou tableau des textes d'attributs
  *       (si 1er argument tableau)
  *     - Tableau complet (si 2e argument)
  *     - null lorsque l’attribut n’existe pas.
@@ -2347,8 +2347,8 @@ function email_valide($adresses) {
  * @filtre
  * @link https://www.spip.net/4134
  *
- * @param string $tags Texte
- * @return string Texte
+ * @param string $tags texte
+ * @return string texte
  **/
 function afficher_enclosures($tags) {
 	$s = [];
@@ -2383,7 +2383,7 @@ function afficher_enclosures($tags) {
  * @filtre
  * @link https://www.spip.net/4187
  *
- * @param string $tags Texte
+ * @param string $tags texte
  * @param string $rels Attribut `rel` à capturer (ou plusieurs séparés par des virgules)
  * @return string Liens trouvés
  **/
@@ -2446,7 +2446,7 @@ function enclosure2microformat($e) {
  * @filtre
  * @see enclosure2microformat() Pour l'inverse
  *
- * @param string $tags Texte HTML ayant des tag `<a>` avec microformat
+ * @param string $tags texte HTML ayant des tag `<a>` avec microformat
  * @return string Tags RSS `<enclosure>`.
  **/
 function microformat2enclosure($tags) {
@@ -2479,7 +2479,7 @@ function microformat2enclosure($tags) {
  *
  * @filtre
  *
- * @param string $tags Texte
+ * @param string $tags texte
  * @return string Tags RSS Atom `<dc:subject>`.
  **/
 function tags2dcsubject($tags) {
@@ -2513,7 +2513,7 @@ function tags2dcsubject($tags) {
  *     ce qui retournerait `<div> un <div> mot </div>` donc.
  *
  * @param string|array $texte
- *     Texte(s) dont on souhaite extraire une balise html
+ *     texte(s) dont on souhaite extraire une balise html
  * @param string $tag
  *     Nom de la balise html à extraire
  * @return void|string|array
@@ -2561,7 +2561,7 @@ function extraire_balise($texte, $tag = 'a') {
  *     tel que demander à extraire `div` dans un texte.
  *
  * @param string|array $texte
- *     Texte(s) dont on souhaite extraire une balise html
+ *     texte(s) dont on souhaite extraire une balise html
  * @param string $tag
  *     Nom de la balise html à extraire
  * @return array
@@ -3147,7 +3147,7 @@ function table_valeur($table, $cle, $defaut = '', $conserver_null = false) {
  * @link http://php.net/manual/fr/function.preg-match.php Pour des infos sur `preg_match()`
  *
  * @param ?string $texte
- *     Texte dans lequel chercher
+ *     texte dans lequel chercher
  * @param string|int $expression
  *     Expression régulière de recherche, sans le délimiteur
  * @param string $modif
@@ -3192,15 +3192,15 @@ function filtre_match_dist(?string $texte, $expression, $modif = 'UuimsS', $capt
  *     ```
  *
  * @param string $texte
- *     Texte
+ *     texte
  * @param string $expression
  *     Expression régulière
  * @param string $replace
- *     Texte de substitution des éléments trouvés
+ *     texte de substitution des éléments trouvés
  * @param string $modif
  *     Modificateurs pour l'expression régulière.
  * @return string
- *     Texte
+ *     texte
  **/
 function replace($texte, $expression, $replace = '', $modif = 'UimsS') {
 	$expression = str_replace('\/', '/', $expression);
@@ -4154,12 +4154,12 @@ function url_rss_forum($texte) {
  * @param string $url
  *   URL du lien
  * @param string $libelle
- *   Texte du lien
+ *   texte du lien
  * @param bool $on
  *   État exposé ou non (génère un lien). Si $on vaut true ou 1 ou ' ', l'etat expose est rendu par un <strong class='on'>...</strong>
  *   Si $on est de la forme span.active.truc par exemple, l'etat expose est rendu par <span class='active truc'>...</span> Seules les balises a, span et strong sont acceptees dans ce cas
  * @param string $class
- *   Classes CSS ajoutées au lien
+ *   classes CSS ajoutées au lien
  * @param string $title
  *   Title ajouté au lien
  * @param string $rel
@@ -4301,7 +4301,7 @@ function prepare_icone_base($type, $lien, $texte, $fond, $fonction = '', $class
 	$class_lien .= " $objet_type";
 	$class_bouton .= " $objet_type";
 
-	// Texte
+	// texte
 	$alt = attribut_html($texte);
 	$title = " title=\"$alt\""; // est-ce pertinent de doubler le alt par un title ?
 
@@ -4355,13 +4355,13 @@ function prepare_icone_base($type, $lien, $texte, $fond, $fonction = '', $class
  * @param string $lien
  *     URL du lien
  * @param string $texte
- *     Texte du lien
+ *     texte du lien
  * @param string $fond
  *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
  * @param string $fonction
  *     Fonction du lien (`edit`, `new`, `del`)
  * @param string $class
- *     Classe CSS, tel que `left`, `right` pour définir un alignement
+ *     classe CSS, tel que `left`, `right` pour définir un alignement
  * @param string $javascript
  *     Javascript ajouté sur le lien
  * @return string
@@ -4389,13 +4389,13 @@ function icone_base($lien, $texte, $fond, $fonction = '', $class = '', $javascri
  * @param string $lien
  *     URL du lien
  * @param string $texte
- *     Texte du lien
+ *     texte du lien
  * @param string $fond
  *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
  * @param string $fonction
  *     Fonction du lien (`edit`, `new`, `del`)
  * @param string $class
- *     Classe CSS à ajouter, tel que `left`, `right`, `center` pour définir un alignement.
+ *     classe CSS à ajouter, tel que `left`, `right`, `center` pour définir un alignement.
  *     Il peut y en avoir plusieurs : `left ajax`
  * @param string $javascript
  *     Javascript ajouté sur le lien
@@ -4435,13 +4435,13 @@ function filtre_icone_verticale_dist($lien, $texte, $fond, $fonction = '', $clas
  * @param string $lien
  *     URL du lien
  * @param string $texte
- *     Texte du lien
+ *     texte du lien
  * @param string $fond
  *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
  * @param string $fonction
  *     Fonction du lien (`edit`, `new`, `del`)
  * @param string $class
- *     Classe CSS à ajouter
+ *     classe CSS à ajouter
  * @param string $javascript
  *     Javascript ajouté sur le lien
  * @return string
@@ -4466,13 +4466,13 @@ function filtre_icone_horizontale_dist($lien, $texte, $fond, $fonction = '', $cl
  * @param string $lien
  *     URL de l'action
  * @param string $texte
- *     Texte du bouton
+ *     texte du bouton
  * @param string $fond
  *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
  * @param string $fonction
  *     Fonction du bouton (`edit`, `new`, `del`)
  * @param string $class
- *     Classes à ajouter au bouton, à l'exception de `ajax` qui est placé sur le formulaire.
+ *     classes à ajouter au bouton, à l'exception de `ajax` qui est placé sur le formulaire.
  * @param string $confirm
  *     Message de confirmation à ajouter en javascript sur le bouton
  * @return string
@@ -4495,15 +4495,15 @@ function filtre_bouton_action_horizontal_dist($lien, $texte, $fond, $fonction =
  * @param string $lien
  *     URL du lien
  * @param string $texte
- *     Texte du lien
+ *     texte du lien
  * @param string $fond
  *     Nom de l'image utilisée
  * @param string $align
- *     Classe CSS d'alignement (`left`, `right`, `center`)
+ *     classe CSS d'alignement (`left`, `right`, `center`)
  * @param string $fonction
  *     Fonction du lien (`edit`, `new`, `del`)
  * @param string $class
- *     Classe CSS à ajouter
+ *     classe CSS à ajouter
  * @param string $javascript
  *     Javascript ajouté sur le lien
  * @return string
@@ -4527,7 +4527,7 @@ function filtre_icone_dist($lien, $texte, $fond, $align = '', $fonction = '', $c
  *     [(#GET{truc}|explode{-})]
  *     ```
  *
- * @param string $a Texte
+ * @param string $a texte
  * @param string $b Séparateur
  * @return array Liste des éléments
  */
@@ -4550,7 +4550,7 @@ function filtre_explode_dist($a, $b) {
  *
  * @param array $a Tableau
  * @param string $b Séparateur
- * @return string Texte
+ * @return string texte
  */
 function filtre_implode_dist($a, $b) {
 	return is_array($a) ? implode($b, $a) : $a;
@@ -5018,7 +5018,7 @@ function objet_info($objet, $info) {
  * @param string $objet
  *     Objet
  * @return mixed|string
- *     Texte traduit du comptage, tel que '3 articles'
+ *     texte traduit du comptage, tel que '3 articles'
  */
 function objet_afficher_nb($nb, $objet) {
 	if (!$nb) {
@@ -5422,7 +5422,7 @@ function spip_affiche_mot_de_passe_masque(?string $passe, bool $afficher_partiel
  * en ne conservant que des caracteres alphanumeriques et un separateur
  *
  * @param string $texte
- *   Texte à transformer en nom machine
+ *   texte à transformer en nom machine
  * @param string $type
  *
  * @param array $options
diff --git a/ecrire/inc/filtres_dates.php b/ecrire/inc/filtres_dates.php
index d5c5f73b2a..a768f5a867 100644
--- a/ecrire/inc/filtres_dates.php
+++ b/ecrire/inc/filtres_dates.php
@@ -32,7 +32,7 @@ if (!defined('_ECRIRE_INC_VERSION')) {
  * @deprecated 4.2
  * @link https://www.spip.net/5516
  * @param string $texte
- *    Texte contenant une date tel que `2008-04`
+ *    texte contenant une date tel que `2008-04`
  * @return string
  *    Date au format SQL tel que `2008-04-01` sinon ''
  **/
@@ -1030,7 +1030,7 @@ function affdate_heure($numdate): string {
  *   - `jour` pour forcer l'affichage du nom du jour
  *   - `hcal` pour avoir un markup microformat abbr
  * @return string
- *     Texte de la date
+ *     texte de la date
  */
 function affdate_debut_fin($date_debut, $date_fin, $horaire = 'oui', $forme = ''): string {
 	$abbr = $jour = '';
diff --git a/ecrire/inc/filtres_mime.php b/ecrire/inc/filtres_mime.php
index f1d05bd05b..5a343816ce 100644
--- a/ecrire/inc/filtres_mime.php
+++ b/ecrire/inc/filtres_mime.php
@@ -130,7 +130,7 @@ function filtre_text_dist($t) {
  *
  * @filtre
  * @param string $t
- *     Texte CSV
+ *     texte CSV
  * @return string
  *     Tableau (formaté en SPIP)
  **/
diff --git a/ecrire/inc/filtres_mini.php b/ecrire/inc/filtres_mini.php
index fdb6f958a3..6b5ee804ac 100644
--- a/ecrire/inc/filtres_mini.php
+++ b/ecrire/inc/filtres_mini.php
@@ -126,7 +126,7 @@ function suivre_lien($url, $lien) {
  *
  * @param string $url URL
  * @param string $base URL de base de destination (par défaut ce sera l'URL de notre site)
- * @return string Texte ou URL (en absolus)
+ * @return string texte ou URL (en absolus)
  **/
 function url_absolue($url, $base = '') {
 	$url = trim((string) $url);
@@ -180,9 +180,9 @@ function protocole_verifier($url_absolue, $protocoles_autorises = ['http','https
  * @uses url_absolue()
  * @link https://www.spip.net/4126
  *
- * @param string $texte Texte
+ * @param string $texte texte
  * @param string $base URL de base de destination (par défaut ce sera l'URL de notre site)
- * @return string Texte avec des URLs absolues
+ * @return string texte avec des URLs absolues
  **/
 function liens_absolus($texte, $base = '') {
 	if (preg_match_all(',(<(a|link|image|img|script)\s[^<>]*(href|src)=[^<>]*>),imsS', $texte, $liens, PREG_SET_ORDER)) {
@@ -216,9 +216,9 @@ function liens_absolus($texte, $base = '') {
  * @link https://www.spip.net/4128
  * @global string $mode_abs_url Pour connaître le mode (url ou texte)
  *
- * @param string $texte Texte ou URL
+ * @param string $texte texte ou URL
  * @param string $base URL de base de destination (par défaut ce sera l'URL de notre site)
- * @return string Texte ou URL (en absolus)
+ * @return string texte ou URL (en absolus)
  **/
 function abs_url($texte, $base = '') {
 	if ($GLOBALS['mode_abs_url'] == 'url') {
diff --git a/ecrire/inc/install.php b/ecrire/inc/install.php
index 5e4eb05d56..7e72034c54 100644
--- a/ecrire/inc/install.php
+++ b/ecrire/inc/install.php
@@ -72,7 +72,7 @@ function install_fichier_connexion($nom, $texte) {
  * @param string $ldap Type d'authentification (cas si 'ldap')
  * @param string $charset Charset de la connexion SQL
  * @return string
- *     Texte du fichier de connexion
+ *     texte du fichier de connexion
  *
  **/
 function install_connexion($adr, $port, $login, $pass, $base, $type, $pref, $ldap = '', $charset = '') {
@@ -244,7 +244,7 @@ function info_etape($titre, $complement = '') {
 /**
  * Retourne le code HTML d'un bouton `suivant>>` pour les phases d'installation
  *
- * @param string $code Texte du bouton
+ * @param string $code texte du bouton
  * @return string Code HTML du bouton
  **/
 function bouton_suivant($code = '') {
diff --git a/ecrire/inc/lister_objets.php b/ecrire/inc/lister_objets.php
index fafcd29f8f..1381c3c1fc 100644
--- a/ecrire/inc/lister_objets.php
+++ b/ecrire/inc/lister_objets.php
@@ -33,7 +33,7 @@ if (!defined('_ECRIRE_INC_VERSION')) {
  * @param string $vue
  *     Nom de l'objet
  * @param array $contexte
- *     Contexte du squelette
+ *     contexte du squelette
  * @param bool $force
  *     Si `true` le titre est affiché même s'il n'y a aucun élément dans la liste.
  * @return string
diff --git a/ecrire/inc/modifier.php b/ecrire/inc/modifier.php
index 12c14dbb8f..c91ffa392f 100644
--- a/ecrire/inc/modifier.php
+++ b/ecrire/inc/modifier.php
@@ -94,7 +94,7 @@ function collecter_requests($include_list, $exclude_list = [], $set = null, $tou
  * @return bool|string
  *     - false  : Aucune modification, aucun champ n'est à modifier
  *     - chaîne vide : Vide si tout s'est bien passé
- *     - chaîne : Texte d'un message d'erreur
+ *     - chaîne : texte d'un message d'erreur
  */
 function objet_modifier_champs($objet, $id_objet, $options, $c = null, $serveur = '') {
 	if (!$id_objet = intval($id_objet)) {
diff --git a/ecrire/inc/presentation.php b/ecrire/inc/presentation.php
index 02eb940ef2..0191c16d2c 100644
--- a/ecrire/inc/presentation.php
+++ b/ecrire/inc/presentation.php
@@ -224,7 +224,7 @@ function onglet($texte, $lien, $onglet_ref, $onglet, $icone = '') {
  *     ```
  *
  * @param string $texte
- *     Texte du lien
+ *     texte du lien
  * @param string $lien
  *     URL du lien
  * @param string $fond
@@ -256,7 +256,7 @@ function icone_verticale($texte, $lien, $fond, $fonction = '', $align = '', $jav
  * @see  filtre_icone_horizontale_dist() Pour l'usage en tant que filtre
  *
  * @param string $texte
- *     Texte du lien
+ *     texte du lien
  * @param string $lien
  *     URL du lien
  * @param string $fond
diff --git a/ecrire/inc/presentation_mini.php b/ecrire/inc/presentation_mini.php
index 080fd188b9..7a6e80c383 100644
--- a/ecrire/inc/presentation_mini.php
+++ b/ecrire/inc/presentation_mini.php
@@ -100,7 +100,7 @@ function debut_droite() {
  * @param string $exec
  *     Nom de la page exec en cours
  * @param array $contexte
- *     Contexte de la page
+ *     contexte de la page
  * @param array|null $auteur
  *     Session de l'auteur. Sera prise sur l'auteur connecté si non indiquée.
  * @return string
@@ -195,7 +195,7 @@ function html_tests_js() {
 /**
  * Retourne la liste des mises à jour de SPIP possibles
  *
- * @return string Texte présentant la liste des mises à jour existantes
+ * @return string texte présentant la liste des mises à jour existantes
  **/
 function info_maj_spip() {
 
diff --git a/ecrire/inc/rubriques.php b/ecrire/inc/rubriques.php
index 6b0efffb28..c763703aab 100644
--- a/ecrire/inc/rubriques.php
+++ b/ecrire/inc/rubriques.php
@@ -10,6 +10,8 @@
  *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+use Spip\Core\Boucle;
+
 /**
  * Fichier gérant l'actualisation et le suivi des rubriques, et de leurs branches
  *
diff --git a/ecrire/inc/texte.php b/ecrire/inc/texte.php
index 94d0fcda97..951d79cba9 100644
--- a/ecrire/inc/texte.php
+++ b/ecrire/inc/texte.php
@@ -212,7 +212,7 @@ function interdire_scripts($arg, $mode_filtre = null) {
  * @see  propre()
  *
  * @param string $letexte
- *     Texte d'origine
+ *     texte d'origine
  * @param bool|string $echapper
  *     Échapper ?
  * @param string|null $connect
@@ -220,7 +220,7 @@ function interdire_scripts($arg, $mode_filtre = null) {
  * @param array $env
  *     Environnement (pour les calculs de modèles)
  * @return string $t
- *     Texte transformé
+ *     texte transformé
  **/
 function typo($letexte, $echapper = true, $connect = null, $env = []) {
 	// Plus vite !
@@ -301,9 +301,9 @@ define('_TYPO_BALISE', ',</?[a-z!][^<>]*[' . preg_quote(_TYPO_PROTEGER) . '][^<>
  * @uses corriger_caracteres()
  * @uses corriger_caracteres()
  *
- * @param string $letexte Texte
+ * @param string $letexte texte
  * @param string $lang Langue
- * @return string Texte
+ * @return string texte
  */
 function corriger_typo($letexte, $lang = '') {
 
@@ -381,8 +381,8 @@ function paragrapher($letexte, $forcer = true) {
  *
  * Ne sert plus
  *
- * @param string $letexte Texte
- * @return string Texte
+ * @param string $letexte texte
+ * @return string texte
  **/
 function traiter_retours_chariots($letexte) {
 	$letexte = preg_replace(",\r\n?,S", "\n", $letexte);
@@ -406,13 +406,13 @@ function traiter_retours_chariots($letexte) {
  * @see  typo()
  *
  * @param string $t
- *     Texte avec des raccourcis SPIP
+ *     texte avec des raccourcis SPIP
  * @param string|null $connect
  *     Nom du connecteur à la bdd
  * @param array $env
  *     Environnement (pour les calculs de modèles)
  * @return string $t
- *     Texte transformé
+ *     texte transformé
  **/
 function propre($t, $connect = null, $env = []) {
 	// les appels directs a cette fonction depuis le php de l'espace
diff --git a/ecrire/inc/texte_mini.php b/ecrire/inc/texte_mini.php
index ebfffc6c85..495e58b14b 100644
--- a/ecrire/inc/texte_mini.php
+++ b/ecrire/inc/texte_mini.php
@@ -371,13 +371,13 @@ function echappe_retour_modeles($letexte, $interdire_scripts = false) {
  * @link https://www.spip.net/4275
  *
  * @param string $texte
- *     Texte à couper
+ *     texte à couper
  * @param int $taille
  *     Taille de la coupe
  * @param string $suite
  *     Points de suite ajoutés.
  * @return string
- *     Texte coupé
+ *     texte coupé
  **/
 function couper($texte, $taille = 50, $suite = null) {
 	if (!($length = strlen($texte)) or $taille <= 0) {
@@ -648,9 +648,9 @@ function echapper_html_suspect($texte, $options = [], $connect = null, $env = []
  * @link https://www.spip.net/4310
  *
  * @param string $t
- *      Texte à sécuriser
+ *      texte à sécuriser
  * @return string
- *      Texte sécurisé
+ *      texte sécurisé
  **/
 function safehtml($t) {
 	static $safehtml;
@@ -738,11 +738,11 @@ function is_html_safe(string $texte): bool {
  *     gérer les autres modèles ?
  *
  * @param string $letexte
- *     Texte à nettoyer
+ *     texte à nettoyer
  * @param string|null $message
  *     Message de remplacement pour chaque image enlevée
  * @return string
- *     Texte sans les modèles d'image
+ *     texte sans les modèles d'image
  **/
 function supprime_img($letexte, $message = null) {
 	if ($message === null) {
diff --git a/ecrire/inc/urls.php b/ecrire/inc/urls.php
index 6f2cdd32fb..04f2fb6a63 100644
--- a/ecrire/inc/urls.php
+++ b/ecrire/inc/urls.php
@@ -42,7 +42,7 @@ include_spip('base/objets');
  * @param string $fond
  *   Fond initial par défaut
  * @param array $contexte
- *   Contexte initial à prendre en compte
+ *   contexte initial à prendre en compte
  * @param bool $assembler
  *   `true` si l'URL correspond à l'URL principale de la page qu'on est en train d'assembler
  *   dans ce cas la fonction redirigera automatiquement si besoin
diff --git a/ecrire/inc/utils.php b/ecrire/inc/utils.php
index 80f8575710..55fe7dea8c 100644
--- a/ecrire/inc/utils.php
+++ b/ecrire/inc/utils.php
@@ -399,7 +399,7 @@ function spip_log($message = null, $name = null) {
  * Enregistrement des journaux
  *
  * @uses inc_journal_dist()
- * @param string $phrase Texte du journal
+ * @param string $phrase texte du journal
  * @param array $opt Tableau d'options
  **/
 function journal($phrase, $opt = []) {
@@ -837,7 +837,7 @@ function test_plugin_actif($plugin) {
  *     - bool force : forcer un retour meme si la chaine n'a pas de traduction
  *     - bool sanitize : nettoyer le html suspect dans les arguments
  * @return string
- *     Texte
+ *     texte
  */
 function _T($texte, $args = [], $options = []) {
 	static $traduire = false;
@@ -902,14 +902,14 @@ function _T($texte, $args = [], $options = []) {
  *     ```
  *
  * @param string $text
- *     Texte
+ *     texte
  * @param array $args
  *     Couples (variable => valeur) à transformer dans le texte
  * @param array $options
  *     - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
  *     - bool sanitize : nettoyer le html suspect dans les arguments
  * @return string
- *     Texte
+ *     texte
  */
 function _L($text, $args = [], $options = []) {
 	$f = $text;
@@ -1308,9 +1308,9 @@ function http_script($script, $src = '', $noscript = '') {
  *
  * @filtre
  * @param string $texte
- *     Texte à échapper
+ *     texte à échapper
  * @return string
- *     Texte échappé
+ *     texte échappé
  **/
 function texte_script(string $texte): string {
 	return str_replace('\'', '\\\'', str_replace('\\', '\\\\', $texte));
diff --git a/ecrire/inc/xml.php b/ecrire/inc/xml.php
index c16c7d8466..3f96e66164 100644
--- a/ecrire/inc/xml.php
+++ b/ecrire/inc/xml.php
@@ -73,7 +73,7 @@ if (!defined('_SPIP_XML_TAG_SPLIT')) {
  * @see spip_xml_aplatit() pour l'inverse
  *
  * @param string $texte
- *     Texte XML
+ *     texte XML
  * @param bool $strict
  *     true pour râler si une balise n'est pas correctement fermée, false sinon.
  * @param bool $clean ?
diff --git a/ecrire/inc_version.php b/ecrire/inc_version.php
index d950d46313..a3eb7155be 100644
--- a/ecrire/inc_version.php
+++ b/ecrire/inc_version.php
@@ -129,7 +129,7 @@ if (!defined('MODULES_IDIOMES')) {
 // *** Fin des define *** //
 
 
-// Inclure l'ecran de securite
+// inclure l'ecran de securite
 if (
 	!defined('_ECRAN_SECURITE')
 	and @file_exists($f = _ROOT_RACINE . _NOM_PERMANENTS_INACCESSIBLES . 'ecran_securite.php')
diff --git a/ecrire/iterateur/condition.php b/ecrire/iterateur/condition.php
index 7a81b3139f..86fc2dfe91 100644
--- a/ecrire/iterateur/condition.php
+++ b/ecrire/iterateur/condition.php
@@ -10,6 +10,8 @@
  *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+use Spip\Core\Boucle;
+
 /**
  * Gestion de l'itérateur CONDITION
  *
diff --git a/ecrire/iterateur/data.php b/ecrire/iterateur/data.php
index 435f61e06e..55455cc894 100644
--- a/ecrire/iterateur/data.php
+++ b/ecrire/iterateur/data.php
@@ -10,6 +10,8 @@
  *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+use Spip\Core\Boucle;
+
 /**
  * Gestion de l'itérateur DATA
  *
diff --git a/ecrire/iterateur/php.php b/ecrire/iterateur/php.php
index 91abdd6557..a86b65f991 100644
--- a/ecrire/iterateur/php.php
+++ b/ecrire/iterateur/php.php
@@ -10,6 +10,8 @@
  *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+use Spip\Core\Boucle;
+
 /**
  * Gestion de l'itérateur PHP
  *
diff --git a/ecrire/iterateur/pour.php b/ecrire/iterateur/pour.php
index 33fa5794d0..36cb2a349b 100644
--- a/ecrire/iterateur/pour.php
+++ b/ecrire/iterateur/pour.php
@@ -10,6 +10,8 @@
  *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+use Spip\Core\Boucle;
+
 /**
  * Gestion de l'itérateur POUR
  *
diff --git a/ecrire/public/assembler.php b/ecrire/public/assembler.php
index 8c82f8b734..3ef7a6b718 100644
--- a/ecrire/public/assembler.php
+++ b/ecrire/public/assembler.php
@@ -388,7 +388,7 @@ function inserer_balise_dynamique($contexte_exec, $contexte_compil) {
  *
  * @param string|array $texte
  * @param bool $echo Faut-il faire echo ou return
- * @param array $contexte_compil Contexte de la compilation
+ * @param array $contexte_compil contexte de la compilation
  * @return string
  */
 function inclure_balise_dynamique($texte, $echo = true, $contexte_compil = []) {
diff --git a/ecrire/public/balises.php b/ecrire/public/balises.php
index 617ef18920..6e530fbb8a 100644
--- a/ecrire/public/balises.php
+++ b/ecrire/public/balises.php
@@ -10,6 +10,8 @@
  *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+use Spip\Core\Champ;
+
 /**
  * Ce fichier regroupe la quasi totalité des définitions de `#BALISES` de SPIP.
  *
@@ -552,7 +554,7 @@ function balise_TOTAL_BOUCLE_dist($p) {
  * Cette balise nécessite donc la présence de ce critère.
  *
  * @balise
- * @link https://www.spip.net/903 Boucles et balises de recherche
+ * @link https://www.spip.net/903 boucles et balises de recherche
  * @see critere_recherche_dist()
  *
  * @param Champ $p
@@ -717,9 +719,9 @@ function balise_EXPOSE_dist($p) {
  * @param Champ $p
  *     Pile au niveau de la balise
  * @param string $on
- *     Texte à afficher si l'élément est exposé (code à écrire tel que "'on'")
+ *     texte à afficher si l'élément est exposé (code à écrire tel que "'on'")
  * @param string $off
- *     Texte à afficher si l'élément n'est pas exposé (code à écrire tel que "''")
+ *     texte à afficher si l'élément n'est pas exposé (code à écrire tel que "''")
  * @return Champ
  *     Pile complétée par le code à générer
  **/
@@ -2540,7 +2542,7 @@ function balise_ACTION_FORMULAIRE($p) {
  * Cette balise s'utilise à la place des liens `action_auteur`, sous la forme
  * `#BOUTON_ACTION{libelle[,url[,class[,confirm[,title[,callback]]]]]}`
  *
- * - libelle  : Texte du bouton
+ * - libelle  : texte du bouton
  * - url      : URL d’action sécurisée
  * - class    : Classes à ajouter au bouton, à l'exception de `ajax` qui est placé sur le formulaire.
  *              Pour d'autres classes sur le formulaire, utiliser le filtre `ajouter_class`
diff --git a/ecrire/public/compiler.php b/ecrire/public/compiler.php
index 45b4c1c1d6..b9ccdaa42a 100644
--- a/ecrire/public/compiler.php
+++ b/ecrire/public/compiler.php
@@ -10,6 +10,11 @@
  *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+use Spip\Core\Boucle;
+use Spip\Core\Contexte;
+use Spip\Core\Inclure;
+use Spip\Core\Texte;
+
 /**
  * Fichier principal du compilateur de squelettes
  *
diff --git a/ecrire/public/composer.php b/ecrire/public/composer.php
index 4c0e7ca827..a3ada38a82 100644
--- a/ecrire/public/composer.php
+++ b/ecrire/public/composer.php
@@ -103,7 +103,7 @@ function public_composer_dist($squelette, $mime_type, $gram, $source, string $co
 			$nom = '';
 		}
 
-		// Contexte de compil inutile a present
+		// contexte de compil inutile a present
 		// (mais la derniere valeur de $boucle est utilisee ci-dessous)
 		$skel_code[$id] = $f;
 	}
diff --git a/ecrire/public/criteres.php b/ecrire/public/criteres.php
index f7a26f088c..4f051262eb 100644
--- a/ecrire/public/criteres.php
+++ b/ecrire/public/criteres.php
@@ -10,6 +10,10 @@
  *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+use Spip\Core\Boucle;
+use Spip\Core\Critere;
+use Spip\Core\Texte;
+
 /**
  * Définition des {criteres} d'une boucle
  *
@@ -868,7 +872,7 @@ function calculer_critere_par_hasard($idb, &$boucles, $crit) {
  * @param array $boucles AST du squelette
  * @param Critere $crit Paramètres du critère dans cette boucle
  * @param array $tri Paramètre en cours du critère
- * @param string $champ Texte suivant l'expression ('titre' dans {par num titre})
+ * @param string $champ texte suivant l'expression ('titre' dans {par num titre})
  * @return string|array Clause pour le Order by (array si erreur)
  */
 function calculer_critere_par_expression_num($idb, &$boucles, $crit, $tri, $champ) {
@@ -906,7 +910,7 @@ function calculer_critere_par_expression_num($idb, &$boucles, $crit, $tri, $cham
  * @param array $boucles AST du squelette
  * @param Critere $crit Paramètres du critère dans cette boucle
  * @param array $tri Paramètre en cours du critère
- * @param string $champ Texte suivant l'expression ('titre' dans {par sinum titre})
+ * @param string $champ texte suivant l'expression ('titre' dans {par sinum titre})
  * @return string|array Clause pour le Order by (array si erreur)
  */
 function calculer_critere_par_expression_sinum($idb, &$boucles, $crit, $tri, $champ) {
@@ -954,7 +958,7 @@ function calculer_critere_par_expression_sinum($idb, &$boucles, $crit, $tri, $ch
  * @param array $boucles AST du squelette
  * @param Critere $crit Paramètres du critère dans cette boucle
  * @param array $tri Paramètre en cours du critère
- * @param string $champ Texte suivant l'expression ('titre' dans {par multi titre})
+ * @param string $champ texte suivant l'expression ('titre' dans {par multi titre})
  * @return string|array Clause pour le Order by (array si erreur)
  */
 function calculer_critere_par_expression_multi($idb, &$boucles, $crit, $tri, $champ) {
@@ -1855,7 +1859,7 @@ function critere_tri_dist($idb, &$boucles, $crit) {
 	$boucle->order[] = "tri_champ_order(\$tri,\$command['from'],\$senstri)";
 }
 
-# Criteres de comparaison
+# criteres de comparaison
 
 /**
  * Compile un critère non déclaré explicitement
diff --git a/ecrire/public/debusquer.php b/ecrire/public/debusquer.php
index a626ec1e57..c15158983d 100644
--- a/ecrire/public/debusquer.php
+++ b/ecrire/public/debusquer.php
@@ -1,5 +1,7 @@
 <?php
 
+use Spip\Core\Contexte;
+
 /***************************************************************************\
  *  SPIP, Système de publication pour l'internet                           *
  *                                                                         *
diff --git a/ecrire/public/decompiler.php b/ecrire/public/decompiler.php
index c956a201f8..8f3d912eb7 100644
--- a/ecrire/public/decompiler.php
+++ b/ecrire/public/decompiler.php
@@ -1,5 +1,7 @@
 <?php
 
+use Spip\Core\Texte;
+
 /***************************************************************************\
  *  SPIP, Système de publication pour l'internet                           *
  *                                                                         *
diff --git a/ecrire/public/fonctions.php b/ecrire/public/fonctions.php
index 8372bdcebf..d224310198 100644
--- a/ecrire/public/fonctions.php
+++ b/ecrire/public/fonctions.php
@@ -44,7 +44,7 @@ include_spip('inc/texte');
  * @param string $descriptif
  *     Descriptif de l'introduction
  * @param string $texte
- *     Texte à utiliser en absence de descriptif
+ *     texte à utiliser en absence de descriptif
  * @param string $longueur
  *     Longueur de l'introduction
  * @param string $connect
@@ -618,7 +618,7 @@ function formate_liste_critere_par_ordre_liste($valeurs, $serveur = '') {
  * @uses chercher_filtre()
  *
  * @param mixed $arg
- *     Texte (le plus souvent) sur lequel appliquer le filtre
+ *     texte (le plus souvent) sur lequel appliquer le filtre
  * @param string $filtre
  *     Nom du filtre à appliquer
  * @param array $args
@@ -626,7 +626,7 @@ function formate_liste_critere_par_ordre_liste($valeurs, $serveur = '') {
  * @param mixed $defaut
  *     Valeur par défaut à retourner en cas d'absence du filtre.
  * @return string
- *     Texte traité par le filtre si le filtre existe,
+ *     texte traité par le filtre si le filtre existe,
  *     Valeur $defaut sinon.
  **/
 function appliquer_filtre_sinon($arg, $filtre, $args, $defaut = '') {
diff --git a/ecrire/public/interfaces.php b/ecrire/public/interfaces.php
index bca42ee365..136d013e8a 100644
--- a/ecrire/public/interfaces.php
+++ b/ecrire/public/interfaces.php
@@ -21,783 +21,12 @@ if (!defined('_ECRIRE_INC_VERSION')) {
 }
 
 
-/**
- * Description d'un contexte de compilation
- *
- * Objet simple pour stocker le nom du fichier, la ligne, la boucle
- * permettant entre autre de localiser le lieu d'une erreur de compilation.
- * Cette structure est nécessaire au traitement d'erreur à l'exécution.
- *
- * Le champ code est inutilisé dans cette classe seule, mais harmonise
- * le traitement d'erreurs.
- *
- * @package SPIP\Core\Compilateur\AST
- */
-class Contexte {
-	/**
-	 * Description du squelette
-	 *
-	 * Sert pour la gestion d'erreur et la production de code dependant du contexte
-	 *
-	 * Peut contenir les index :
-	 *
-	 * - nom : Nom du fichier de cache
-	 * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser)
-	 * - sourcefile : Chemin du squelette
-	 * - squelette : Code du squelette
-	 * - id_mere : Identifiant de la boucle parente
-	 * - documents : Pour embed et img dans les textes
-	 * - session : Pour un cache sessionné par auteur
-	 * - niv : Niveau de tabulation
-	 */
-	public array $descr = [];
-
-	/** Identifiant de la boucle */
-	public string $id_boucle = '';
-
-	/** Numéro de ligne dans le code source du squelette */
-	public int $ligne = 0;
-
-	/** Langue d'exécution */
-	public string $lang = '';
-
-	/** Résultat de la compilation: toujours une expression PHP */
-	public string $code = '';
-}
-
-
-/**
- * Description d'un texte
- *
- * @package SPIP\Core\Compilateur\AST
- **/
-class Texte {
-	/** Type de noeud */
-	public string $type = 'texte';
-
-	/** Le texte */
-	public string $texte;
-
-	/**
-	 * Contenu avant le texte.
-	 *
-	 * Vide ou apostrophe simple ou double si le texte en était entouré
-	 *
-	 * @var string|array
-	 */
-	public $avant = '';
-
-	/**
-	 * Contenu après le texte.
-	 *
-	 * Vide ou apostrophe simple ou double si le texte en était entouré
-	 *
-	 * @var string|array
-	 */
-	public $apres = '';
-
-	/** Numéro de ligne dans le code source du squelette */
-	public int $ligne = 0;
-}
-
-/**
- * Description d'une inclusion de squelette
- *
- * @package SPIP\Core\Compilateur\AST
- **/
-class Inclure {
-	/** Type de noeud */
-	public string $type = 'include';
-
-	/**
-	 * Nom d'un fichier inclu
-	 *
-	 * - Objet Texte si inclusion d'un autre squelette
-	 * - chaîne si inclusion d'un fichier PHP directement
-	 *
-	 * @var string|Texte
-	 */
-	public $texte;
-
-	/**
-	 * Inutilisé, propriété générique de l'AST
-	 *
-	 * @var string|array
-	 */
-	public $avant = '';
-
-	/**
-	 * Inutilisé, propriété générique de l'AST
-	 *
-	 * @var string|array
-	 */
-	public $apres = '';
-
-	/** Numéro de ligne dans le code source du squelette */
-	public int $ligne = 0;
-
-	/**
-	 * Valeurs des paramètres
-	 *
-	 * FIXME: type unique.
-	 * @var false|array
-	 *     - false: erreur de syntaxe
-	 */
-	public $param = [];
-
-	/** Source des filtres (compatibilité) (?) */
-	public array $fonctions = [];
-
-	/**
-	 * Description du squelette
-	 *
-	 * Sert pour la gestion d'erreur et la production de code dependant du contexte
-	 *
-	 * Peut contenir les index :
-	 *
-	 * - nom : Nom du fichier de cache
-	 * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser)
-	 * - sourcefile : Chemin du squelette
-	 * - squelette : Code du squelette
-	 * - id_mere : Identifiant de la boucle parente
-	 * - documents : Pour embed et img dans les textes
-	 * - session : Pour un cache sessionné par auteur
-	 * - niv : Niveau de tabulation
-	 */
-	public array $descr = [];
-}
-
-
-/**
- * Description d'une boucle
- *
- * @package SPIP\Core\Compilateur\AST
- **/
-class Boucle {
-	/** Type de noeud */
-	public string $type = 'boucle';
-
-	/** Identifiant de la boucle */
-	public string $id_boucle;
-
-	/** Identifiant de la boucle parente */
-	public string $id_parent = '';
-
-	/** Un nom explicite qui peut être affecté manuellement à certaines boucles générées */
-	public string $nom = '';
-
-	/**
-	 * Partie avant toujours affichee
-	 *
-	 * @var string|array
-	 */
-	public $preaff = '';
-
-	/**
-	 * Partie optionnelle avant
-	 *
-	 * @var string|array
-	 */
-	public $avant = '';
-
-	/**
-	 * Pour chaque élément
-	 *
-	 * @var string|array
-	 */
-	public $milieu = '';
-
-	/**
-	 * Partie optionnelle après
-	 *
-	 * @var string|array
-	 */
-	public $apres = '';
-
-	/**
-	 * Partie alternative, si pas de résultat dans la boucle
-	 *
-	 * @var string|array
-	 */
-	public $altern = '';
-
-	/**
-	 * Partie apres toujours affichee
-	 *
-	 * @var string|array
-	 */
-	public $postaff = '';
-
-
-	/**
-	 * La boucle doit-elle sélectionner la langue ?
-	 *
-	 * Valeurs : '', 'oui', 'non'
-	 */
-	public string $lang_select = '';
-
-	/**
-	 * Alias de table d'application de la requête ou nom complet de la table SQL
-	 *
-	 * FIXME: un seul typage (string ?)
-	 *
-	 * @var string|false|null
-	 */
-	public $type_requete = null;
-
-	/**
-	 * La table est elle optionnelle ?
-	 *
-	 * Si oui, aucune erreur ne sera générée si la table demandée n'est pas présente
-	 */
-	public bool $table_optionnelle = false;
-
-	/**
-	 * Nom du fichier de connexion
-	 */
-	public string $sql_serveur = '';
-
-	/**
-	 * Paramètres de la boucle
-	 *
-	 * Description des paramètres passés à la boucle, qui servent ensuite
-	 * au calcul des critères
-	 *
-	 * FIXME: type unique.
-	 * @var false|array
-	 *     - false: erreur de syntaxe
-	 */
-	public $param = [];
-
-	/**
-	 * Critères de la boucle
-	 *
-	 * FIXME: type array unique.
-	 *
-	 * @var string|Critere[]
-	 * - string: phrasage (code brut). Il reste si erreur de critère
-	 * - array: analyse correcte des critères...
-	 */
-	public $criteres = [];
-
-	/**
-	 * Textes insérés entre 2 éléments de boucle (critère inter)
-	 *
-	 * @var string[]
-	 */
-	public array $separateur = [];
-
-	/**
-	 * Liste des jointures possibles avec cette table
-	 *
-	 * Les jointures par défaut de la table sont complétées en priorité
-	 * des jointures déclarées explicitement sur la boucle
-	 *
-	 * @see base_trouver_table_dist()
-	 */
-	public array $jointures = [];
-
-	/**
-	 * Jointures explicites avec cette table
-	 *
-	 * Ces jointures sont utilisées en priorité par rapport aux jointures
-	 * normales possibles pour retrouver les colonnes demandées extérieures
-	 * à la boucle.
-	 *
-	 * @var string|bool
-	 */
-	public $jointures_explicites = false;
-
-	/**
-	 * Nom de la variable PHP stockant le noms de doublons utilisés "$doublons_index"
-	 */
-	public string $doublons = '';
-
-	/**
-	 * Code PHP ajouté au début de chaque itération de boucle.
-	 *
-	 * Utilisé entre autre par les critères {pagination}, {n-a,b}, {a/b}...
-	 */
-	public string $partie = '';
-
-	/**
-	 * Nombre de divisions de la boucle, d'éléments à afficher,
-	 * ou de soustractions d'éléments à faire
-	 *
-	 * Dans les critères limitant le nombre d'éléments affichés
-	 * {a,b}, {a,n-b}, {a/b}, {pagination b}, b est affecté à total_parties.
-	 */
-	public string $total_parties = '';
-
-	/**
-	 * Code PHP ajouté avant l'itération de boucle.
-	 *
-	 * Utilisé entre autre par les critères {pagination}, {a,b}, {a/b}
-	 * pour initialiser les variables de début et de fin d'itération.
-	 */
-	public string $mode_partie = '';
-
-	/**
-	 * Identifiant d'une boucle qui appelle celle-ci de manière récursive
-	 *
-	 * Si une boucle est appelée de manière récursive quelque part par
-	 * une autre boucle comme <BOUCLE_rec(boucle_identifiant) />, cette
-	 * boucle (identifiant) reçoit dans cette propriété l'identifiant
-	 * de l'appelant (rec)
-	 */
-	public string $externe = '';
-
-	// champs pour la construction de la requete SQL
-
-	/**
-	 * Liste des champs à récupérer par la boucle
-	 *
-	 * Expression 'table.nom_champ' ou calculée 'nom_champ AS x'
-	 *
-	 * @var string[]
-	 */
-	public array $select = [];
-
-	/**
-	 * Liste des alias / tables SQL utilisées dans la boucle
-	 *
-	 * L'index est un identifiant (xx dans spip_xx assez souvent) qui servira
-	 * d'alias au nom de la table ; la valeur est le nom de la table SQL désirée.
-	 *
-	 * L'index 0 peut définir le type de sources de données de l'itérateur DATA
-	 *
-	 * @var string[]
-	 */
-	public array $from = [];
-
-	/**
-	 * Liste des alias / type de jointures utilisées dans la boucle
-	 *
-	 * L'index est le nom d'alias (comme pour la propriété $from), et la valeur
-	 * un type de jointure parmi 'INNER', 'LEFT', 'RIGHT', 'OUTER'.
-	 *
-	 * Lorsque le type n'est pas déclaré pour un alias, c'est 'INNER'
-	 * qui sera utilisé par défaut (créant donc un INNER JOIN).
-	 *
-	 * @var string[]
-	 */
-	public array $from_type = [];
-
-	/**
-	 * Liste des conditions WHERE de la boucle
-	 *
-	 * Permet de restreindre les éléments retournés par une boucle
-	 * en fonctions des conditions transmises dans ce tableau.
-	 *
-	 * Ce tableau peut avoir plusieurs niveaux de profondeur.
-	 *
-	 * Les éléments du premier niveau sont reliés par des AND, donc
-	 * chaque élément ajouté directement au where par
-	 * $boucle->where[] = array(...) ou $boucle->where[] = "'expression'"
-	 * est une condition AND en plus.
-	 *
-	 * Par contre, lorsqu'on indique un tableau, il peut décrire des relations
-	 * internes différentes. Soit $expr un tableau d'expressions quelconques de 3 valeurs :
-	 * $expr = array(operateur, val1, val2)
-	 *
-	 * Ces 3 valeurs sont des expressions PHP. L'index 0 désigne l'opérateur
-	 * à réaliser tel que :
-	 *
-	 * - "'='" , "'>='", "'<'", "'IN'", "'REGEXP'", "'LIKE'", ... :
-	 *    val1 et val2 sont des champs et valeurs à utiliser dans la comparaison
-	 *    suivant cet ordre : "val1 operateur val2".
-	 *    Exemple : $boucle->where[] = array("'='", "'articles.statut'", "'\"publie\"'");
-	 * - "'AND'", "'OR'", "'NOT'" :
-	 *    dans ce cas val1 et val2 sont également des expressions
-	 *    de comparaison complètes, et peuvent être eux-même des tableaux comme $expr
-	 *    Exemples :
-	 *    $boucle->where[] = array("'OR'", $expr1, $expr2);
-	 *    $boucle->where[] = array("'NOT'", $expr); // val2 n'existe pas avec NOT
-	 *
-	 * D'autres noms sont possibles pour l'opérateur (le nombre de valeurs diffère) :
-	 * - "'SELF'", "'SUBSELECT'" : indiquent des sous requêtes
-	 * - "'?'" : indique une condition à faire évaluer (val1 ? val2 : val3)
-	 */
-	public array $where = [];
-
-	public array $join = [];
-	public array $having = [];
-	public $limit = '';
-	public array $group = [];
-	public array $order = [];
-	public array $default_order = [];
-	public string $date = 'date';
-	public string $hash = '';
-	public $in = '';
-	public bool $sous_requete = false;
-
-	/**
-	 * Code PHP qui sera ajouté en tout début de la fonction de boucle
-	 *
-	 * Il sert à insérer le code calculant une hierarchie
-	 */
-	public string $hierarchie = '';
-
-	// champs pour la construction du corps PHP
-
-	/**
-	 * Description des sources de données de la boucle
-	 *
-	 * Description des données de la boucle issu de trouver_table
-	 * dans le cadre de l'itérateur SQL et contenant au moins l'index 'field'.
-	 *
-	 * @see base_trouver_table_dist()
-	 */
-	public array $show = [];
-
-	/**
-	 * Nom de la table SQL principale de la boucle, sans son préfixe
-	 */
-	public string $id_table = '';
-
-	/**
-	 * Nom de la clé primaire de la table SQL principale de la boucle
-	 */
-	public string $primary = '';
-
-	/**
-	 * Code PHP compilé de la boucle
-	 *
-	 * FIXME: un seul type (string ?)
-	 *
-	 * - false: boucle fautive ?
-	 *
-	 * @var string|false
-	 */
-	public $return = '';
-
-	public $numrows = false;
-	public $cptrows = false;
-
-	/**
-	 * Description du squelette
-	 *
-	 * Sert pour la gestion d'erreur et la production de code dependant du contexte
-	 *
-	 * Peut contenir les index :
-	 *
-	 * - nom : Nom du fichier de cache
-	 * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser)
-	 * - sourcefile : Chemin du squelette
-	 * - squelette : Code du squelette
-	 * - id_mere : Identifiant de la boucle parente
-	 * - documents : Pour embed et img dans les textes
-	 * - session : Pour un cache sessionné par auteur
-	 * - niv : Niveau de tabulation
-	 */
-	public array $descr = [];
-
-	/** Numéro de ligne dans le code source du squelette */
-	public int $ligne = 0;
-
-
-	/**
-	 * table pour stocker les modificateurs de boucle tels que tout, plat ...,
-	 * utilisable par les plugins egalement
-	 *
-	 * @var array<string, mixed>
-	 */
-	public array $modificateur = [];
-
-	/**
-	 * Type d'itérateur utilisé pour cette boucle
-	 *
-	 * - 'SQL' dans le cadre d'une boucle sur une table SQL
-	 * - 'DATA' pour l'itérateur DATA, ...
-	 *
-	 * @var string
-	 */
-	public string $iterateur = ''; // type d'iterateur
-
-	/**
-	 * @var array $debug Textes qui seront insérés dans l’entête de boucle du mode debug
-	 */
-	public array $debug = [];
-
-	/**
-	 * Index de la boucle dont le champ présent dans cette boucle est originaire,
-	 * notamment si le champ a été trouve dans une boucle parente
-	 *
-	 * Tableau nom du champ => index de boucle
-	*/
-	public array $index_champ = [];
-
-	/** Résultat de la compilation (?) (sert au débusqueur) */
-	public string $code = '';
-
-	/** Source des filtres (compatibilité) (?) */
-	public array $fonctions = [];
-
-	// obsoletes, conserves provisoirement pour compatibilite
-	public $tout = false;
-	public $plat = false;
-	public $lien = false;
-}
-
-/**
- * Description d'un critère de boucle
- *
- * Sous-noeud de Boucle
- *
- * @package SPIP\Core\Compilateur\AST
- **/
-class Critere {
-	/** Type de noeud */
-	public string $type = 'critere';
-
-	/** Opérateur (>, <, >=, IN, ...) */
-	public ?string $op;
-
-	/** Présence d'une négation (truc !op valeur) */
-	public bool $not = false;
-
-	/** Présence d'une exclusion (!truc op valeur) */
-	public string $exclus = '';
-
-	/** Présence d'une condition dans le critère (truc ?) */
-	public bool $cond = false;
-
-	/**
-	 * Paramètres du critère
-	 * - $param[0] : élément avant l'opérateur
-	 * - $param[1..n] : éléments après l'opérateur
-	 *
-	 * FIXME: type unique.
-	 * @var false|array
-	 *     - false: erreur de syntaxe
-	 */
-	public $param = [];
-
-	/** Numéro de ligne dans le code source du squelette */
-	public int $ligne = 0;
-}
-
-/**
- * Description d'un champ (balise SPIP)
- *
- * @package SPIP\Core\Compilateur\AST
- **/
-class Champ {
-	/** Type de noeud */
-	public string $type = 'champ';
-
-	/** Nom du champ demandé. Exemple 'ID_ARTICLE' */
-	public ?string $nom_champ;
-
-	/** Identifiant de la boucle parente si explicité */
-	public ?string $nom_boucle = '';
-
-	/**
-	 * Partie optionnelle avant
-	 *
-	 * @var null|string|array
-	 */
-	public $avant;
-
-	/**
-	 * Partie optionnelle après
-	 *
-	 * @var null|string|array
-	 */
-	public $apres;
-
-	/**
-	 * Étoiles : annuler des automatismes
-	 *
-	 * - '*' annule les filtres automatiques
-	 * - '**' annule en plus les protections de scripts
-	 */
-	public ?string $etoile;
-
-	/**
-	 * Arguments et filtres explicites sur la balise
-	 *
-	 * - $param[0] contient les arguments de la balise
-	 * - $param[1..n] contient les filtres à appliquer à la balise
-	 *
-	 * FIXME: type unique.
-	 * @var false|array
-	 *     - false: erreur de syntaxe
-	 */
-	public $param = [];
-
-	/** Source des filtres (compatibilité) (?) */
-	public array $fonctions = [];
-
-	/**
-	 * Identifiant de la boucle
-	 *
-	 * @var string
-	 */
-	public $id_boucle = '';
-
-	/**
-	 * AST du squelette, liste de toutes les boucles
-	 *
-	 * @var Boucle[]
-	 */
-	public array $boucles;
-
-	/** Alias de table d'application de la requête ou nom complet de la table SQL */
-	public ?string $type_requete;
-
-	/** Résultat de la compilation: toujours une expression PHP */
-	public string $code = '';
-
-	/**
-	 * Interdire les scripts
-	 *
-	 * false si on est sûr de cette balise
-	 *
-	 * @see interdire_scripts()
-	 */
-	public bool $interdire_scripts = true;
-
-	/**
-	 * Description du squelette
-	 *
-	 * Sert pour la gestion d'erreur et la production de code dependant du contexte
-	 *
-	 * Peut contenir les index :
-	 *
-	 * - nom : Nom du fichier de cache
-	 * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser)
-	 * - sourcefile : Chemin du squelette
-	 * - squelette : Code du squelette
-	 * - id_mere : Identifiant de la boucle parente
-	 * - documents : Pour embed et img dans les textes
-	 * - session : Pour un cache sessionné par auteur
-	 * - niv : Niveau de tabulation
-	 */
-	public array $descr = [];
-
-	/** Numéro de ligne dans le code source du squelette*/
-	public int $ligne = 0;
-
-	/** Drapeau pour reperer les balises calculées par une fonction explicite */
-	public bool $balise_calculee = false;
-}
-
-
-/**
- * Description d'une chaîne de langue
- **/
-class Idiome {
-	/** Type de noeud */
-	public string $type = 'idiome';
-
-	/** Clé de traduction demandée. Exemple 'item_oui' */
-	public string $nom_champ = '';
-
-	/** Module de langue où chercher la clé de traduction. Exemple 'medias' */
-	public string $module = '';
-
-	/** Arguments à passer à la chaîne */
-	public array $arg = [];
-
-	/**
-	 * Filtres à appliquer au résultat
-	 *
-	 * FIXME: type unique.
-	 * @var false|array
-	 *     - false: erreur de syntaxe
-	 */
-	public $param = [];
-
-	/** Source des filtres (compatibilité) (?) */
-	public array $fonctions = [];
-
-	/**
-	 * Inutilisé, propriété générique de l'AST
-	 *
-	 * @var string|array
-	 */
-	public $avant = '';
-
-	/**
-	 * Inutilisé, propriété générique de l'AST
-	 *
-	 * @var string|array
-	 */
-	public $apres = '';
-
-	/** Identifiant de la boucle */
-	public string $id_boucle = '';
-
-	/**
-	 * AST du squelette, liste de toutes les boucles
-	 *
-	 * @var Boucle[]
-	 */
-	public array $boucles;
-
-	/** Alias de table d'application de la requête ou nom complet de la table SQL */
-	public ?string $type_requete;
-
-	/** Résultat de la compilation: toujours une expression PHP */
-	public string $code = '';
-
-	/**
-	 * Interdire les scripts
-	 *
-	 * @see interdire_scripts()
-	 */
-	public bool $interdire_scripts = false;
-
-	/**
-	 * Description du squelette
-	 *
-	 * Sert pour la gestion d'erreur et la production de code dependant du contexte
-	 *
-	 * Peut contenir les index :
-	 * - nom : Nom du fichier de cache
-	 * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser)
-	 * - sourcefile : Chemin du squelette
-	 * - squelette : Code du squelette
-	 * - id_mere : Identifiant de la boucle parente
-	 * - documents : Pour embed et img dans les textes
-	 * - session : Pour un cache sessionné par auteur
-	 * - niv : Niveau de tabulation
-	 */
-	public array $descr = [];
-
-	/** Numéro de ligne dans le code source du squelette */
-	public int $ligne = 0;
-}
-
-/**
- * Description d'un texte polyglotte (<multi>)
- *
- * @package SPIP\Core\Compilateur\AST
- **/
-class Polyglotte {
-	/** Type de noeud */
-	public string $type = 'polyglotte';
-
-	/**
-	 * Tableau des traductions possibles classées par langue
-	 *
-	 * Tableau code de langue => texte
-	 */
-	public array $traductions = [];
-
-	/** Numéro de ligne dans le code source du squelette */
-	public int $ligne = 0;
-}
-
-
 global $table_criteres_infixes;
 $table_criteres_infixes = ['<', '>', '<=', '>=', '==', '===', '!=', '!==', '<>', '?'];
 
 global $exception_des_connect;
 $exception_des_connect[] = ''; // ne pas transmettre le connect='' par les inclure
 
-
 /**
  * Déclarer les interfaces de la base pour le compilateur
  *
diff --git a/ecrire/public/jointures.php b/ecrire/public/jointures.php
index c3ca975326..fc0b1506f8 100644
--- a/ecrire/public/jointures.php
+++ b/ecrire/public/jointures.php
@@ -1,5 +1,7 @@
 <?php
 
+use Spip\Core\Boucle;
+
 /***************************************************************************\
  *  SPIP, Système de publication pour l'internet                           *
  *                                                                         *
diff --git a/ecrire/public/normaliser.php b/ecrire/public/normaliser.php
index 508ff94df2..39a6f3e219 100644
--- a/ecrire/public/normaliser.php
+++ b/ecrire/public/normaliser.php
@@ -1,5 +1,7 @@
 <?php
 
+use Spip\Core\Texte;
+
 /***************************************************************************\
  *  SPIP, Système de publication pour l'internet                           *
  *                                                                         *
diff --git a/ecrire/public/phraser_html.php b/ecrire/public/phraser_html.php
index 97a9ecc713..5c00ad6dc6 100644
--- a/ecrire/public/phraser_html.php
+++ b/ecrire/public/phraser_html.php
@@ -10,6 +10,14 @@
  *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+use Spip\Core\Boucle;
+use Spip\Core\Champ;
+use Spip\Core\Critere;
+use Spip\Core\Idiome;
+use Spip\Core\Inclure;
+use Spip\Core\Polyglotte;
+use Spip\Core\Texte;
+
 /**
  * Phraseur d'un squelette ayant une syntaxe SPIP/HTML
  *
diff --git a/ecrire/public/references.php b/ecrire/public/references.php
index a0abebfb6b..b57bc1d292 100644
--- a/ecrire/public/references.php
+++ b/ecrire/public/references.php
@@ -10,6 +10,9 @@
  *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+use Spip\Core\Boucle;
+use Spip\Core\Champ;
+
 /**
  * Fonctions de recherche et de reservation dans l'arborescence des boucles
  *
@@ -482,7 +485,7 @@ function calculer_champ($p) {
  * @return Champ
  *     Pile complétée par le code PHP pour l'exécution de la balise et de ses filtres
  **/
-function calculer_balise(string $nom, \Champ $p): \Champ {
+function calculer_balise(string $nom, Champ $p): Champ {
 
 	// S'agit-t-il d'une balise_XXXX[_dist]() ?
 	if ($f = charger_fonction($nom, 'balise', true)) {
@@ -684,7 +687,7 @@ function calculer_balise_dynamique($p, $nom, $l, $supp = []) {
  * @return array
  *     Liste des codes PHP d'éxecution des balises collectées
  **/
-function collecter_balise_dynamique(array $l, \Champ &$p, string $nom): array {
+function collecter_balise_dynamique(array $l, Champ &$p, string $nom): array {
 	$args = [];
 	foreach ($l as $c) {
 		if ($c === null) {
diff --git a/ecrire/public/sandbox.php b/ecrire/public/sandbox.php
index b1446f2d54..1c87deb908 100644
--- a/ecrire/public/sandbox.php
+++ b/ecrire/public/sandbox.php
@@ -34,11 +34,11 @@ if (!defined('_ECRIRE_INC_VERSION')) {
  * dans les squelettes
  *
  * @param string $texte
- *     Texte à composer
+ *     texte à composer
  * @param Champ $p
  *     Balise qui appelle ce texte
  * @return string
- *     Texte
+ *     texte
  */
 function sandbox_composer_texte($texte, &$p) {
 	$code = "'" . str_replace(['\\', "'"], ['\\\\', "\\'"], $texte) . "'";
diff --git a/ecrire/req/mysql.php b/ecrire/req/mysql.php
index d9d2bd09d1..9b9cd07729 100644
--- a/ecrire/req/mysql.php
+++ b/ecrire/req/mysql.php
@@ -203,7 +203,7 @@ function spip_mysql_get_charset($charset = [], $serveur = '', $requeter = true)
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return mysqli_result|bool|string|array
  *     - mysqli_result|bool : Si requête exécutée
- *     - string : Texte de la requête si on ne l'exécute pas
+ *     - string : texte de la requête si on ne l'exécute pas
  *     - array : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
  */
 function spip_mysql_query($query, $serveur = '', $requeter = true) {
@@ -291,7 +291,7 @@ function spip_mysql_query($query, $serveur = '', $requeter = true) {
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return array|bool|string
- *     - string : Texte de la requête si on ne l'exécute pas
+ *     - string : texte de la requête si on ne l'exécute pas
  *     - bool   : Si requête exécutée
  *     - array  : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
  */
@@ -323,7 +323,7 @@ function spip_mysql_optimize($table, $serveur = '', $requeter = true) {
 /**
  * Retourne une explication de requête (Explain) MySQL
  *
- * @param string $query Texte de la requête
+ * @param string $query texte de la requête
  * @param string $serveur Nom de la connexion
  * @param bool $requeter inutilisé
  * @return array           Tableau de l'explication
@@ -363,7 +363,7 @@ function spip_mysql_explain($query, $serveur = '', $requeter = true) {
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return array|bool|resource|string
- *     - string : Texte de la requête si on ne l'exécute pas
+ *     - string : texte de la requête si on ne l'exécute pas
  *     - ressource si requête exécutée, ressource pour fetch()
  *     - false si la requête exécutée a ratée
  *     - array  : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
@@ -410,8 +410,8 @@ function spip_mysql_select(
  *   0+x avec un champ x commencant par des chiffres est converti par MySQL
  *   en le nombre qui commence x. Pas portable malheureusement, on laisse pour le moment.
  *
- * @param string|array $orderby Texte du orderby à préparer
- * @return string Texte du orderby préparé
+ * @param string|array $orderby texte du orderby à préparer
+ * @return string texte du orderby préparé
  */
 function spip_mysql_order($orderby) {
 	return (is_array($orderby)) ? join(', ', $orderby) : $orderby;
@@ -426,7 +426,7 @@ function spip_mysql_order($orderby) {
  *
  * @param array|string $v
  *     Description des contraintes
- *     - string : Texte du where
+ *     - string : texte du where
  *     - sinon tableau : A et B peuvent être de type string ou array,
  *       OP et C sont de type string :
  *       - array(A) : A est le texte du where
@@ -468,7 +468,7 @@ function calculer_mysql_where($v) {
  * @param string $expression Mot clé de l'expression, tel que "WHERE" ou "ORDER BY"
  * @param array|string $v Données de l'expression
  * @param string $join Si les données sont un tableau, elles seront groupées par cette jointure
- * @return string            Texte de l'expression, une partie donc, du texte la requête.
+ * @return string            texte de l'expression, une partie donc, du texte la requête.
  */
 function calculer_mysql_expression($expression, $v, $join = 'AND') {
 	if (empty($v)) {
@@ -790,7 +790,7 @@ function spip_mysql_create_base($nom, $serveur = '', $requeter = true) {
  * @param string $nom
  *    Nom de la vue à creer
  * @param string $query_select
- *     Texte de la requête de sélection servant de base à la vue
+ *     texte de la requête de sélection servant de base à la vue
  * @param string $serveur
  *     Nom du connecteur
  * @param bool $requeter
@@ -825,7 +825,7 @@ function spip_mysql_create_view($nom, $query_select, $serveur = '', $requeter =
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return bool|string
- *     - string Texte de la requête si demandé
+ *     - string texte de la requête si demandé
  *     - true si la requête a réussie, false sinon
  */
 function spip_mysql_drop_table($table, $exist = '', $serveur = '', $requeter = true) {
@@ -844,7 +844,7 @@ function spip_mysql_drop_table($table, $exist = '', $serveur = '', $requeter = t
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return bool|string
- *     - string Texte de la requête si demandé
+ *     - string texte de la requête si demandé
  *     - true si la requête a réussie, false sinon
  */
 function spip_mysql_drop_view($view, $exist = '', $serveur = '', $requeter = true) {
@@ -881,7 +881,7 @@ function spip_mysql_showbase($match, $serveur = '', $requeter = true) {
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return bool|string
- *     - string Texte de la requête si demandée,
+ *     - string texte de la requête si demandée,
  *     - true si la requête a réussie, false sinon
  */
 function spip_mysql_repair($table, $serveur = '', $requeter = true) {
@@ -937,7 +937,7 @@ define('_MYSQL_RE_SHOW_TABLE', '/^[^(),]*\(((?:[^()]*\((?:[^()]*\([^()]*\))?[^()
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return array|string
  *     - chaîne vide si pas de description obtenue
- *     - string Texte de la requête si demandé
+ *     - string texte de la requête si demandé
  *     - array description de la table sinon
  */
 function spip_mysql_showtable($nom_table, $serveur = '', $requeter = true) {
@@ -1074,7 +1074,7 @@ function spip_mysql_seek($r, $row_number, $serveur = '', $requeter = true) {
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return int|string
- *     - String Texte de la requête si demandé
+ *     - string texte de la requête si demandé
  *     - int Nombre de lignes (0 si la requête n'a pas réussie)
  **/
 function spip_mysql_countsel(
@@ -1209,8 +1209,8 @@ function spip_mysql_free($r, $serveur = '', $requeter = true) {
  *     Exécuter la requête, sinon la retourner
  * @return bool|string|int|array
  *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur,
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur,
  *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
  **/
 function spip_mysql_insert($table, $champs, $valeurs, $desc = [], $serveur = '', $requeter = true) {
@@ -1286,8 +1286,8 @@ function spip_mysql_insert($table, $champs, $valeurs, $desc = [], $serveur = '',
  *     Exécuter la requête, sinon la retourner
  * @return bool|string|int|array
  *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur,
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur,
  *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
  **/
 function spip_mysql_insertq($table, $couples = [], $desc = [], $serveur = '', $requeter = true) {
@@ -1331,8 +1331,8 @@ function spip_mysql_insertq($table, $couples = [], $desc = [], $serveur = '', $r
  *     Exécuter la requête, sinon la retourner
  * @return int|bool|string
  *     - int|true identifiant du dernier élément inséré (si possible), ou true, si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function spip_mysql_insertq_multi($table, $tab_couples = [], $desc = [], $serveur = '', $requeter = true) {
 
@@ -1467,8 +1467,8 @@ function spip_mysql_updateq($table, $champs, $where = '', $desc = [], $serveur =
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return bool|string
  *     - int : nombre de suppressions réalisées,
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function spip_mysql_delete($table, $where = '', $serveur = '', $requeter = true) {
 	$res = spip_mysql_query(
@@ -1511,8 +1511,8 @@ function spip_mysql_delete($table, $where = '', $serveur = '', $requeter = true)
  *     Exécuter la requête, sinon la retourner
  * @return bool|string
  *     - true si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function spip_mysql_replace($table, $couples, $desc = [], $serveur = '', $requeter = true) {
 	return spip_mysql_query("REPLACE $table (" . join(',', array_keys($couples)) . ') VALUES (' . join(
@@ -1543,8 +1543,8 @@ function spip_mysql_replace($table, $couples, $desc = [], $serveur = '', $requet
  *     Exécuter la requête, sinon la retourner
  * @return bool|string
  *     - true si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function spip_mysql_replace_multi($table, $tab_couples, $desc = [], $serveur = '', $requeter = true) {
 	$cles = '(' . join(',', array_keys($tab_couples[0])) . ')';
@@ -1566,7 +1566,7 @@ function spip_mysql_replace_multi($table, $tab_couples, $desc = [], $serveur = '
  *
  * @param string $objet Colonne ayant le texte
  * @param string $lang Langue à extraire
- * @return string       Texte de sélection pour la requête
+ * @return string       texte de sélection pour la requête
  */
 function spip_mysql_multi($objet, $lang) {
 	$lengthlang = strlen("[$lang]");
@@ -1616,7 +1616,7 @@ function spip_mysql_hex($v) {
  * comme le fait `_q()` mais pour MySQL avec ses spécificités
  *
  * @param string|array|number $v
- *     Texte, nombre ou tableau à échapper
+ *     texte, nombre ou tableau à échapper
  * @param string $type
  *     Description du type attendu
  *    (par exemple description SQL de la colonne recevant la donnée)
@@ -1690,9 +1690,9 @@ function spip_mysql_in($val, $valeurs, $not = '', $serveur = '', $requeter = tru
 /**
  * Renvoie les bons echappements (mais pas sur les fonctions comme NOW())
  *
- * @param string|number $v Texte ou nombre à échapper
+ * @param string|number $v texte ou nombre à échapper
  * @param string $type Type de donnée attendue, description SQL de la colonne de destination
- * @return string|number     Texte ou nombre échappé
+ * @return string|number     texte ou nombre échappé
  */
 function spip_mysql_cite($v, $type) {
 	if (!$type) {
diff --git a/ecrire/req/sqlite_generique.php b/ecrire/req/sqlite_generique.php
index 3e285c08fb..fb05be28f2 100644
--- a/ecrire/req/sqlite_generique.php
+++ b/ecrire/req/sqlite_generique.php
@@ -445,7 +445,7 @@ function spip_sqlite_alter($query, $serveur = '', $requeter = true) {
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return array|null|resource|string
- *     - string Texte de la requête si demandée
+ *     - string texte de la requête si demandée
  *     - true si la requête réussie, false sinon.
  */
 function spip_sqlite_create(
@@ -517,7 +517,7 @@ function spip_sqlite_create_base($nom, $serveur = '', $option = true) {
  * @param string $nom
  *    Nom de la vue a creer
  * @param string $query_select
- *     Texte de la requête de sélection servant de base à la vue
+ *     texte de la requête de sélection servant de base à la vue
  * @param string $serveur
  *     Nom du connecteur
  * @param bool $requeter
@@ -667,7 +667,7 @@ function spip_sqlite_count($r, $serveur = '', $requeter = true) {
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return int|bool|string
- *     - String Texte de la requête si demandé
+ *     - string texte de la requête si demandé
  *     - int Nombre de lignes
  *     - false si la requête a échouée
  **/
@@ -708,8 +708,8 @@ function spip_sqlite_countsel(
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return bool|string
  *     - int : nombre de suppressions réalisées,
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function spip_sqlite_delete($table, $where = '', $serveur = '', $requeter = true) {
 	$res = spip_sqlite_query(
@@ -741,7 +741,7 @@ function spip_sqlite_delete($table, $where = '', $serveur = '', $requeter = true
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return bool|string
- *     - string Texte de la requête si demandé
+ *     - string texte de la requête si demandé
  *     - true si la requête a réussie, false sinon
  */
 function spip_sqlite_drop_table($table, $exist = '', $serveur = '', $requeter = true) {
@@ -765,7 +765,7 @@ function spip_sqlite_drop_table($table, $exist = '', $serveur = '', $requeter =
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return bool|string
- *     - string Texte de la requête si demandé
+ *     - string texte de la requête si demandé
  *     - true si la requête a réussie, false sinon
  */
 function spip_sqlite_drop_view($view, $exist = '', $serveur = '', $requeter = true) {
@@ -891,7 +891,7 @@ function spip_sqlite_errno($serveur = '') {
 /**
  * Retourne une explication de requête (Explain) SQLite
  *
- * @param string $query Texte de la requête
+ * @param string $query texte de la requête
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return array|string|bool
@@ -1068,8 +1068,8 @@ function spip_sqlite_in($val, $valeurs, $not = '', $serveur = '', $requeter = tr
  *     Exécuter la requête, sinon la retourner
  * @return bool|string|int|array
  *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur,
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur,
  *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
  **/
 function spip_sqlite_insert($table, $champs, $valeurs, $desc = [], $serveur = '', $requeter = true) {
@@ -1107,8 +1107,8 @@ function spip_sqlite_insert($table, $champs, $valeurs, $desc = [], $serveur = ''
  *     Exécuter la requête, sinon la retourner
  * @return bool|string|int|array
  *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur,
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur,
  *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
  **/
 function spip_sqlite_insertq($table, $couples = [], $desc = [], $serveur = '', $requeter = true) {
@@ -1152,9 +1152,9 @@ function spip_sqlite_insertq($table, $couples = [], $desc = [], $serveur = '', $
  * @param bool $requeter
  *     Exécuter la requête, sinon la retourner
  * @return bool|string
- *     - True en cas de succès,
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - true en cas de succès,
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function spip_sqlite_insertq_multi($table, $tab_couples = [], $desc = [], $serveur = '', $requeter = true) {
 	if (!$desc) {
@@ -1323,7 +1323,7 @@ function spip_sqlite_listdbs($serveur = '', $requeter = true) {
  *
  * @param string $objet Colonne ayant le texte
  * @param string $lang Langue à extraire
- * @return string       Texte de sélection pour la requête
+ * @return string       texte de sélection pour la requête
  */
 function spip_sqlite_multi($objet, $lang) {
 	$r = 'EXTRAIRE_MULTI(' . $objet . ", '" . $lang . "') AS multi";
@@ -1362,7 +1362,7 @@ function spip_sqlite_optimize($table, $serveur = '', $requeter = true) {
  * mais pour SQLite avec ses spécificités
  *
  * @param string|array|number $v
- *     Texte, nombre ou tableau à échapper
+ *     texte, nombre ou tableau à échapper
  * @param string $type
  *     Description du type attendu
  *    (par exemple description SQL de la colonne recevant la donnée)
@@ -1481,8 +1481,8 @@ function spip_sqlite_repair($table, $serveur = '', $requeter = true) {
  *     Exécuter la requête, sinon la retourner
  * @return bool|string
  *     - true si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function spip_sqlite_replace($table, $couples, $desc = [], $serveur = '', $requeter = true) {
 	if (!$desc) {
@@ -1528,8 +1528,8 @@ function spip_sqlite_replace($table, $couples, $desc = [], $serveur = '', $reque
  *     Exécuter la requête, sinon la retourner
  * @return bool|string
  *     - true si réussite
- *     - Texte de la requête si demandé,
- *     - False en cas d'erreur.
+ *     - texte de la requête si demandé,
+ *     - false en cas d'erreur.
  **/
 function spip_sqlite_replace_multi($table, $tab_couples, $desc = [], $serveur = '', $requeter = true) {
 
@@ -1560,7 +1560,7 @@ function spip_sqlite_replace_multi($table, $tab_couples, $desc = [], $serveur =
  * @param string $serveur Nom de la connexion
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return array|bool|resource|string
- *     - string : Texte de la requête si on ne l'exécute pas
+ *     - string : texte de la requête si on ne l'exécute pas
  *     - ressource si requête exécutée, ressource pour fetch()
  *     - false si la requête exécutée a ratée
  *     - array  : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
@@ -1747,7 +1747,7 @@ define('_SQLITE_RE_SHOW_TABLE', '/^[^(),]*\(((?:[^()]*\((?:[^()]*\([^()]*\))?[^(
  * @param bool $requeter Exécuter la requête, sinon la retourner
  * @return array|string
  *     - chaîne vide si pas de description obtenue
- *     - string Texte de la requête si demandé
+ *     - string texte de la requête si demandé
  *     - array description de la table sinon
  */
 function spip_sqlite_showtable($nom_table, $serveur = '', $requeter = true) {
@@ -2053,9 +2053,9 @@ function _sqlite_link($serveur = '') {
 /**
  * Renvoie les bons echappements (mais pas sur les fonctions comme NOW())
  *
- * @param string|number $v Texte ou nombre à échapper
+ * @param string|number $v texte ou nombre à échapper
  * @param string $type Type de donnée attendue, description SQL de la colonne de destination
- * @return string|number     Texte ou nombre échappé
+ * @return string|number     texte ou nombre échappé
  */
 function _sqlite_calculer_cite($v, $type) {
 	if ($type) {
@@ -2119,7 +2119,7 @@ function _sqlite_calculer_cite($v, $type) {
  * @param string $expression Mot clé de l'expression, tel que "WHERE" ou "ORDER BY"
  * @param array|string $v Données de l'expression
  * @param string $join Si les données sont un tableau, elles seront groupées par cette jointure
- * @return string            Texte de l'expression, une partie donc, du texte la requête.
+ * @return string            texte de l'expression, une partie donc, du texte la requête.
  */
 function _sqlite_calculer_expression($expression, $v, $join = 'AND') {
 	if (empty($v)) {
@@ -2148,8 +2148,8 @@ function _sqlite_calculer_expression($expression, $v, $join = 'AND') {
  * @note
  *   Pas besoin de conversion pour 0+x comme il faudrait pour mysql.
  *
- * @param string|array $orderby Texte du orderby à préparer
- * @return string Texte du orderby préparé
+ * @param string|array $orderby texte du orderby à préparer
+ * @return string texte du orderby préparé
  */
 function _sqlite_calculer_order($orderby) {
 	return (is_array($orderby)) ? join(', ', $orderby) : $orderby;
@@ -2194,7 +2194,7 @@ function _sqlite_calculer_select_as($args) {
  *
  * @param array|string $v
  *     Description des contraintes
- *     - string : Texte du where
+ *     - string : texte du where
  *     - sinon tableau : A et B peuvent être de type string ou array,
  *       OP et C sont de type string :
  *       - array(A) : A est le texte du where
@@ -2863,7 +2863,7 @@ class spip_sqlite {
  */
 
 class sqlite_requeteur {
-	/** @var string Texte de la requête */
+	/** @var string texte de la requête */
 	public $query = ''; // la requete
 	/** @var string Nom de la connexion */
 	public $serveur = '';
@@ -2987,7 +2987,7 @@ class sqlite_requeteur {
  * (fonction pour proteger les textes)
  */
 class sqlite_traducteur {
-	/** @var string $query Texte de la requête */
+	/** @var string $query texte de la requête */
 	public $query = '';
 	/** @var string $prefixe Préfixe des tables */
 	public $prefixe = '';
@@ -3160,7 +3160,7 @@ class sqlite_traducteur {
 	 * par `DATE ... strtotime`
 	 *
 	 * @param array $matches Captures
-	 * @return string Texte de date compris par SQLite
+	 * @return string texte de date compris par SQLite
 	 */
 	public function _remplacerDateParTime($matches) {
 		$op = strtoupper($matches[1] == 'ADD') ? '+' : '-';
@@ -3173,7 +3173,7 @@ class sqlite_traducteur {
 	 * par `CASE WHEN table=i THEN n ... ELSE 0 END`
 	 *
 	 * @param array $matches Captures
-	 * @return string Texte de liste ordonnée compris par SQLite
+	 * @return string texte de liste ordonnée compris par SQLite
 	 */
 	public function _remplacerFieldParCase($matches) {
 		$fields = substr($matches[0], 6, -1); // ne recuperer que l'interieur X de field(X)
diff --git a/ecrire/src/Core/Balise.php b/ecrire/src/Core/Balise.php
new file mode 100644
index 0000000000..0d2d832497
--- /dev/null
+++ b/ecrire/src/Core/Balise.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Spip\Core;
+
+/**
+ * Description d'une Balise.
+ */
+class Balise
+{
+	/** Type de noeud */
+	public string $type = 'balise';
+
+	/** Nom du champ demandé. Exemple 'ID_ARTICLE' */
+	public ?string $nom_champ;
+
+	/** Identifiant de la boucle parente si explicité */
+	public ?string $nom_boucle = '';
+
+	/**
+	 * Partie optionnelle avant
+	 *
+	 * @var null|string|array
+	 */
+	public $avant;
+
+	/**
+	 * Partie optionnelle après
+	 *
+	 * @var null|string|array
+	 */
+	public $apres;
+
+	/**
+	 * Étoiles : annuler des automatismes
+	 *
+	 * - '*' annule les filtres automatiques
+	 * - '**' annule en plus les protections de scripts
+	 */
+	public ?string $etoile;
+
+	/**
+	 * Arguments et filtres explicites sur la balise
+	 *
+	 * - $param[0] contient les arguments de la balise
+	 * - $param[1..n] contient les filtres à appliquer à la balise
+	 *
+	 * FIXME: type unique.
+	 * @var false|array
+	 *     - false: erreur de syntaxe
+	 */
+	public $param = [];
+
+	/** Source des filtres (compatibilité) (?) */
+	public array $fonctions = [];
+
+	/**
+	 * Identifiant de la boucle
+	 *
+	 * @var string
+	 */
+	public $id_boucle = '';
+
+	/**
+	 * AST du squelette, liste de toutes les boucles
+	 *
+	 * @var Boucle[]
+	 */
+	public array $boucles;
+
+	/** Alias de table d'application de la requête ou nom complet de la table SQL */
+	public ?string $type_requete;
+
+	/** Résultat de la compilation: toujours une expression PHP */
+	public string $code = '';
+
+	/**
+	 * Interdire les scripts
+	 *
+	 * false si on est sûr de cette balise
+	 *
+	 * @see interdire_scripts()
+	 */
+	public bool $interdire_scripts = true;
+
+	/**
+	 * Description du squelette
+	 *
+	 * Sert pour la gestion d'erreur et la production de code dependant du contexte
+	 *
+	 * Peut contenir les index :
+	 *
+	 * - nom : Nom du fichier de cache
+	 * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser)
+	 * - sourcefile : Chemin du squelette
+	 * - squelette : Code du squelette
+	 * - id_mere : Identifiant de la boucle parente
+	 * - documents : Pour embed et img dans les textes
+	 * - session : Pour un cache sessionné par auteur
+	 * - niv : Niveau de tabulation
+	 */
+	public array $descr = [];
+
+	/** Numéro de ligne dans le code source du squelette*/
+	public int $ligne = 0;
+
+	/** Drapeau pour reperer les balises calculées par une fonction explicite */
+	public bool $balise_calculee = false;
+}
diff --git a/ecrire/src/Core/Boucle.php b/ecrire/src/Core/Boucle.php
new file mode 100644
index 0000000000..3c08c5bb63
--- /dev/null
+++ b/ecrire/src/Core/Boucle.php
@@ -0,0 +1,373 @@
+<?php
+
+namespace Spip\Core;
+
+/**
+ * Description d'une boucle
+ */
+class Boucle
+{
+	/** Type de noeud */
+	public string $type = 'boucle';
+
+	/** Identifiant de la boucle */
+	public string $id_boucle;
+
+	/** Identifiant de la boucle parente */
+	public string $id_parent = '';
+
+	/** Un nom explicite qui peut être affecté manuellement à certaines boucles générées */
+	public string $nom = '';
+
+	/**
+	 * Partie avant toujours affichee
+	 *
+	 * @var string|array
+	 */
+	public $preaff = '';
+
+	/**
+	 * Partie optionnelle avant
+	 *
+	 * @var string|array
+	 */
+	public $avant = '';
+
+	/**
+	 * Pour chaque élément
+	 *
+	 * @var string|array
+	 */
+	public $milieu = '';
+
+	/**
+	 * Partie optionnelle après
+	 *
+	 * @var string|array
+	 */
+	public $apres = '';
+
+	/**
+	 * Partie alternative, si pas de résultat dans la boucle
+	 *
+	 * @var string|array
+	 */
+	public $altern = '';
+
+	/**
+	 * Partie apres toujours affichee
+	 *
+	 * @var string|array
+	 */
+	public $postaff = '';
+
+
+	/**
+	 * La boucle doit-elle sélectionner la langue ?
+	 *
+	 * Valeurs : '', 'oui', 'non'
+	 */
+	public string $lang_select = '';
+
+	/**
+	 * Alias de table d'application de la requête ou nom complet de la table SQL
+	 *
+	 * FIXME: un seul typage (string ?)
+	 *
+	 * @var string|false|null
+	 */
+	public $type_requete = null;
+
+	/**
+	 * La table est elle optionnelle ?
+	 *
+	 * Si oui, aucune erreur ne sera générée si la table demandée n'est pas présente
+	 */
+	public bool $table_optionnelle = false;
+
+	/**
+	 * Nom du fichier de connexion
+	 */
+	public string $sql_serveur = '';
+
+	/**
+	 * Paramètres de la boucle
+	 *
+	 * Description des paramètres passés à la boucle, qui servent ensuite
+	 * au calcul des critères
+	 *
+	 * FIXME: type unique.
+	 * @var false|array
+	 *     - false: erreur de syntaxe
+	 */
+	public $param = [];
+
+	/**
+	 * Critères de la boucle
+	 *
+	 * FIXME: type array unique.
+	 *
+	 * @var string|Critere[]
+	 * - string: phrasage (code brut). Il reste si erreur de critère
+	 * - array: analyse correcte des critères...
+	 */
+	public $criteres = [];
+
+	/**
+	 * Textes insérés entre 2 éléments de boucle (critère inter)
+	 *
+	 * @var string[]
+	 */
+	public array $separateur = [];
+
+	/**
+	 * Liste des jointures possibles avec cette table
+	 *
+	 * Les jointures par défaut de la table sont complétées en priorité
+	 * des jointures déclarées explicitement sur la boucle
+	 *
+	 * @see base_trouver_table_dist()
+	 */
+	public array $jointures = [];
+
+	/**
+	 * Jointures explicites avec cette table
+	 *
+	 * Ces jointures sont utilisées en priorité par rapport aux jointures
+	 * normales possibles pour retrouver les colonnes demandées extérieures
+	 * à la boucle.
+	 *
+	 * @var string|bool
+	 */
+	public $jointures_explicites = false;
+
+	/**
+	 * Nom de la variable PHP stockant le noms de doublons utilisés "$doublons_index"
+	 */
+	public string $doublons = '';
+
+	/**
+	 * Code PHP ajouté au début de chaque itération de boucle.
+	 *
+	 * Utilisé entre autre par les critères {pagination}, {n-a,b}, {a/b}...
+	 */
+	public string $partie = '';
+
+	/**
+	 * Nombre de divisions de la boucle, d'éléments à afficher,
+	 * ou de soustractions d'éléments à faire
+	 *
+	 * Dans les critères limitant le nombre d'éléments affichés
+	 * {a,b}, {a,n-b}, {a/b}, {pagination b}, b est affecté à total_parties.
+	 */
+	public string $total_parties = '';
+
+	/**
+	 * Code PHP ajouté avant l'itération de boucle.
+	 *
+	 * Utilisé entre autre par les critères {pagination}, {a,b}, {a/b}
+	 * pour initialiser les variables de début et de fin d'itération.
+	 */
+	public string $mode_partie = '';
+
+	/**
+	 * Identifiant d'une boucle qui appelle celle-ci de manière récursive
+	 *
+	 * Si une boucle est appelée de manière récursive quelque part par
+	 * une autre boucle comme <BOUCLE_rec(boucle_identifiant) />, cette
+	 * boucle (identifiant) reçoit dans cette propriété l'identifiant
+	 * de l'appelant (rec)
+	 */
+	public string $externe = '';
+
+	// champs pour la construction de la requete SQL
+
+	/**
+	 * Liste des champs à récupérer par la boucle
+	 *
+	 * Expression 'table.nom_champ' ou calculée 'nom_champ AS x'
+	 *
+	 * @var string[]
+	 */
+	public array $select = [];
+
+	/**
+	 * Liste des alias / tables SQL utilisées dans la boucle
+	 *
+	 * L'index est un identifiant (xx dans spip_xx assez souvent) qui servira
+	 * d'alias au nom de la table ; la valeur est le nom de la table SQL désirée.
+	 *
+	 * L'index 0 peut définir le type de sources de données de l'itérateur DATA
+	 *
+	 * @var string[]
+	 */
+	public array $from = [];
+
+	/**
+	 * Liste des alias / type de jointures utilisées dans la boucle
+	 *
+	 * L'index est le nom d'alias (comme pour la propriété $from), et la valeur
+	 * un type de jointure parmi 'INNER', 'LEFT', 'RIGHT', 'OUTER'.
+	 *
+	 * Lorsque le type n'est pas déclaré pour un alias, c'est 'INNER'
+	 * qui sera utilisé par défaut (créant donc un INNER JOIN).
+	 *
+	 * @var string[]
+	 */
+	public array $from_type = [];
+
+	/**
+	 * Liste des conditions WHERE de la boucle
+	 *
+	 * Permet de restreindre les éléments retournés par une boucle
+	 * en fonctions des conditions transmises dans ce tableau.
+	 *
+	 * Ce tableau peut avoir plusieurs niveaux de profondeur.
+	 *
+	 * Les éléments du premier niveau sont reliés par des AND, donc
+	 * chaque élément ajouté directement au where par
+	 * $boucle->where[] = array(...) ou $boucle->where[] = "'expression'"
+	 * est une condition AND en plus.
+	 *
+	 * Par contre, lorsqu'on indique un tableau, il peut décrire des relations
+	 * internes différentes. Soit $expr un tableau d'expressions quelconques de 3 valeurs :
+	 * $expr = array(operateur, val1, val2)
+	 *
+	 * Ces 3 valeurs sont des expressions PHP. L'index 0 désigne l'opérateur
+	 * à réaliser tel que :
+	 *
+	 * - "'='" , "'>='", "'<'", "'IN'", "'REGEXP'", "'LIKE'", ... :
+	 *    val1 et val2 sont des champs et valeurs à utiliser dans la comparaison
+	 *    suivant cet ordre : "val1 operateur val2".
+	 *    Exemple : $boucle->where[] = array("'='", "'articles.statut'", "'\"publie\"'");
+	 * - "'AND'", "'OR'", "'NOT'" :
+	 *    dans ce cas val1 et val2 sont également des expressions
+	 *    de comparaison complètes, et peuvent être eux-même des tableaux comme $expr
+	 *    Exemples :
+	 *    $boucle->where[] = array("'OR'", $expr1, $expr2);
+	 *    $boucle->where[] = array("'NOT'", $expr); // val2 n'existe pas avec NOT
+	 *
+	 * D'autres noms sont possibles pour l'opérateur (le nombre de valeurs diffère) :
+	 * - "'SELF'", "'SUBSELECT'" : indiquent des sous requêtes
+	 * - "'?'" : indique une condition à faire évaluer (val1 ? val2 : val3)
+	 */
+	public array $where = [];
+
+	public array $join = [];
+	public array $having = [];
+	public $limit = '';
+	public array $group = [];
+	public array $order = [];
+	public array $default_order = [];
+	public string $date = 'date';
+	public string $hash = '';
+	public $in = '';
+	public bool $sous_requete = false;
+
+	/**
+	 * Code PHP qui sera ajouté en tout début de la fonction de boucle
+	 *
+	 * Il sert à insérer le code calculant une hierarchie
+	 */
+	public string $hierarchie = '';
+
+	// champs pour la construction du corps PHP
+
+	/**
+	 * Description des sources de données de la boucle
+	 *
+	 * Description des données de la boucle issu de trouver_table
+	 * dans le cadre de l'itérateur SQL et contenant au moins l'index 'field'.
+	 *
+	 * @see base_trouver_table_dist()
+	 */
+	public array $show = [];
+
+	/**
+	 * Nom de la table SQL principale de la boucle, sans son préfixe
+	 */
+	public string $id_table = '';
+
+	/**
+	 * Nom de la clé primaire de la table SQL principale de la boucle
+	 */
+	public string $primary = '';
+
+	/**
+	 * Code PHP compilé de la boucle
+	 *
+	 * FIXME: un seul type (string ?)
+	 *
+	 * - false: boucle fautive ?
+	 *
+	 * @var string|false
+	 */
+	public $return = '';
+
+	public $numrows = false;
+	public $cptrows = false;
+
+	/**
+	 * Description du squelette
+	 *
+	 * Sert pour la gestion d'erreur et la production de code dependant du contexte
+	 *
+	 * Peut contenir les index :
+	 *
+	 * - nom : Nom du fichier de cache
+	 * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser)
+	 * - sourcefile : Chemin du squelette
+	 * - squelette : Code du squelette
+	 * - id_mere : Identifiant de la boucle parente
+	 * - documents : Pour embed et img dans les textes
+	 * - session : Pour un cache sessionné par auteur
+	 * - niv : Niveau de tabulation
+	 */
+	public array $descr = [];
+
+	/** Numéro de ligne dans le code source du squelette */
+	public int $ligne = 0;
+
+
+	/**
+	 * table pour stocker les modificateurs de boucle tels que tout, plat ...,
+	 * utilisable par les plugins egalement
+	 *
+	 * @var array<string, mixed>
+	 */
+	public array $modificateur = [];
+
+	/**
+	 * Type d'itérateur utilisé pour cette boucle
+	 *
+	 * - 'SQL' dans le cadre d'une boucle sur une table SQL
+	 * - 'DATA' pour l'itérateur DATA, ...
+	 *
+	 * @var string
+	 */
+	public string $iterateur = ''; // type d'iterateur
+
+	/**
+	 * @var array $debug Textes qui seront insérés dans l’entête de boucle du mode debug
+	 */
+	public array $debug = [];
+
+	/**
+	 * Index de la boucle dont le champ présent dans cette boucle est originaire,
+	 * notamment si le champ a été trouve dans une boucle parente
+	 *
+	 * Tableau nom du champ => index de boucle
+	*/
+	public array $index_champ = [];
+
+	/** Résultat de la compilation (?) (sert au débusqueur) */
+	public string $code = '';
+
+	/** Source des filtres (compatibilité) (?) */
+	public array $fonctions = [];
+
+	// obsoletes, conserves provisoirement pour compatibilite
+	public $tout = false;
+	public $plat = false;
+	public $lien = false;
+}
diff --git a/ecrire/src/Core/Champ.php b/ecrire/src/Core/Champ.php
new file mode 100644
index 0000000000..5b20df21ff
--- /dev/null
+++ b/ecrire/src/Core/Champ.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Spip\Core;
+
+/**
+ * Retro compatiblité un Champ, c'est une Balise.
+ */
+class Champ extends Balise
+{
+	/**
+	 * {@inheritDoc}
+	 */
+	public string $type = 'champ';
+}
diff --git a/ecrire/src/Core/Contexte.php b/ecrire/src/Core/Contexte.php
new file mode 100644
index 0000000000..985b228e67
--- /dev/null
+++ b/ecrire/src/Core/Contexte.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Spip\Core;
+
+/**
+ * Description d'un contexte de compilation
+ *
+ * Objet simple pour stocker le nom du fichier, la ligne, la boucle
+ * permettant entre autre de localiser le lieu d'une erreur de compilation.
+ * Cette structure est nécessaire au traitement d'erreur à l'exécution.
+ *
+ * Le champ code est inutilisé dans cette classe seule, mais harmonise
+ * le traitement d'erreurs.
+ */
+class Contexte
+{
+	/**
+	 * Description du squelette
+	 *
+	 * Sert pour la gestion d'erreur et la production de code dependant du contexte
+	 *
+	 * Peut contenir les index :
+	 *
+	 * - nom : Nom du fichier de cache
+	 * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser)
+	 * - sourcefile : Chemin du squelette
+	 * - squelette : Code du squelette
+	 * - id_mere : Identifiant de la boucle parente
+	 * - documents : Pour embed et img dans les textes
+	 * - session : Pour un cache sessionné par auteur
+	 * - niv : Niveau de tabulation
+	 */
+	public array $descr = [];
+
+	/** Identifiant de la boucle */
+	public string $id_boucle = '';
+
+	/** Numéro de ligne dans le code source du squelette */
+	public int $ligne = 0;
+
+	/** Langue d'exécution */
+	public string $lang = '';
+
+	/** Résultat de la compilation: toujours une expression PHP */
+	public string $code = '';
+}
diff --git a/ecrire/src/Core/Critere.php b/ecrire/src/Core/Critere.php
new file mode 100644
index 0000000000..5c703ed119
--- /dev/null
+++ b/ecrire/src/Core/Critere.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Spip\Core;
+
+/**
+ * Description d'un critère de boucle.
+ *
+ * Sous-noeud de Boucle
+ **/
+class Critere {
+	/** Type de noeud */
+	public string $type = 'critere';
+
+	/** Opérateur (>, <, >=, IN, ...) */
+	public ?string $op;
+
+	/** Présence d'une négation (truc !op valeur) */
+	public bool $not = false;
+
+	/** Présence d'une exclusion (!truc op valeur) */
+	public string $exclus = '';
+
+	/** Présence d'une condition dans le critère (truc ?) */
+	public bool $cond = false;
+
+	/**
+	 * Paramètres du critère
+	 * - $param[0] : élément avant l'opérateur
+	 * - $param[1..n] : éléments après l'opérateur
+	 *
+	 * FIXME: type unique.
+	 * @var false|array
+	 *     - false: erreur de syntaxe
+	 */
+	public $param = [];
+
+	/** Numéro de ligne dans le code source du squelette */
+	public int $ligne = 0;
+}
diff --git a/ecrire/src/Core/Idiome.php b/ecrire/src/Core/Idiome.php
new file mode 100644
index 0000000000..c10d8c71a7
--- /dev/null
+++ b/ecrire/src/Core/Idiome.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Spip\Core;
+
+/**
+ * Description d'une chaîne de langue
+ **/
+class Idiome
+{
+	/** Type de noeud */
+	public string $type = 'idiome';
+
+	/** Clé de traduction demandée. Exemple 'item_oui' */
+	public string $nom_champ = '';
+
+	/** Module de langue où chercher la clé de traduction. Exemple 'medias' */
+	public string $module = '';
+
+	/** Arguments à passer à la chaîne */
+	public array $arg = [];
+
+	/**
+	 * Filtres à appliquer au résultat
+	 *
+	 *
+	 * * FIXME: type unique.
+	 * @var false|array
+	 *     - false: erreur de syntaxe
+	 */
+	public $param = [];
+
+	/** Source des filtres (compatibilité) (?) */
+	public array $fonctions = [];
+
+	/**
+	 * Inutilisé, propriété générique de l'AST
+	 *
+	 * @var string|array
+	 */
+	public $avant = '';
+
+	/**
+	 * Inutilisé, propriété générique de l'AST
+	 *
+	 * @var string|array
+	 */
+	public $apres = '';
+
+	/** Identifiant de la boucle */
+	public string $id_boucle = '';
+
+	/**
+	 * AST du squelette, liste de toutes les boucles
+	 *
+	 * @var Boucle[]
+	 */
+	public array $boucles;
+
+	/** Alias de table d'application de la requête ou nom complet de la table SQL */
+	public ?string $type_requete;
+
+	/** Résultat de la compilation: toujours une expression PHP */
+	public string $code = '';
+
+	/**
+	 * Interdire les scripts
+	 *
+	 * @see interdire_scripts()
+	 */
+	public bool $interdire_scripts = false;
+
+	/**
+	 * Description du squelette
+	 *
+	 * Sert pour la gestion d'erreur et la production de code dependant du contexte
+	 *
+	 * Peut contenir les index :
+	 * - nom : Nom du fichier de cache
+	 * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser)
+	 * - sourcefile : Chemin du squelette
+	 * - squelette : Code du squelette
+	 * - id_mere : Identifiant de la boucle parente
+	 * - documents : Pour embed et img dans les textes
+	 * - session : Pour un cache sessionné par auteur
+	 * - niv : Niveau de tabulation
+	 */
+	public array $descr = [];
+
+	/** Numéro de ligne dans le code source du squelette */
+	public int $ligne = 0;
+}
diff --git a/ecrire/src/Core/Inclure.php b/ecrire/src/Core/Inclure.php
new file mode 100644
index 0000000000..c28ca0168b
--- /dev/null
+++ b/ecrire/src/Core/Inclure.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Spip\Core;
+
+/**
+ * Description d'une inclusion de squelette.
+ **/
+class Inclure
+{
+	/** Type de noeud */
+	public string $type = 'include';
+
+	/**
+	 * Nom d'un fichier inclu
+	 *
+	 * - Objet Texte si inclusion d'un autre squelette
+	 * - chaîne si inclusion d'un fichier PHP directement
+	 *
+	 * @var string|Texte
+	 */
+	public $texte;
+
+	/**
+	 * Inutilisé, propriété générique de l'AST
+	 *
+	 * @var string|array
+	 */
+	public $avant = '';
+
+	/**
+	 * Inutilisé, propriété générique de l'AST
+	 *
+	 * @var string|array
+	 */
+	public $apres = '';
+
+	/** Numéro de ligne dans le code source du squelette */
+	public int $ligne = 0;
+
+	/**
+	 * Valeurs des paramètres
+	 *
+	 * FIXME: type unique.
+	 * @var false|array
+	 *     - false: erreur de syntaxe
+	 */
+	public $param = [];
+
+	/** Source des filtres (compatibilité) (?) */
+	public array $fonctions = [];
+
+	/**
+	 * Description du squelette
+	 *
+	 * Sert pour la gestion d'erreur et la production de code dependant du contexte
+	 *
+	 * Peut contenir les index :
+	 *
+	 * - nom : Nom du fichier de cache
+	 * - gram : Nom de la grammaire du squelette (détermine le phraseur à utiliser)
+	 * - sourcefile : Chemin du squelette
+	 * - squelette : Code du squelette
+	 * - id_mere : Identifiant de la boucle parente
+	 * - documents : Pour embed et img dans les textes
+	 * - session : Pour un cache sessionné par auteur
+	 * - niv : Niveau de tabulation
+	 */
+	public array $descr = [];
+}
diff --git a/ecrire/src/Core/Polyglotte.php b/ecrire/src/Core/Polyglotte.php
new file mode 100644
index 0000000000..0b0880393b
--- /dev/null
+++ b/ecrire/src/Core/Polyglotte.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Spip\Core;
+
+/**
+ * Description d'un texte polyglotte.
+ *
+ * a.k.a. <multi>
+ **/
+class Polyglotte
+{
+	/** Type de noeud */
+	public string $type = 'polyglotte';
+
+	/**
+	 * Tableau des traductions possibles classées par langue
+	 *
+	 * Tableau code de langue => texte
+	 */
+	public array $traductions = [];
+
+	/** Numéro de ligne dans le code source du squelette */
+	public int $ligne = 0;
+}
diff --git a/ecrire/src/Core/Tests/BaliseTest.php b/ecrire/src/Core/Tests/BaliseTest.php
new file mode 100644
index 0000000000..0abb301a70
--- /dev/null
+++ b/ecrire/src/Core/Tests/BaliseTest.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Spip\Core\Tests;
+
+class BaliseTest
+{
+
+}
diff --git a/ecrire/src/Core/Texte.php b/ecrire/src/Core/Texte.php
new file mode 100644
index 0000000000..3eb1bad818
--- /dev/null
+++ b/ecrire/src/Core/Texte.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Spip\Core;
+
+/**
+ * Description d'un texte.
+ **/
+class Texte
+{
+	/** Type de noeud */
+	public string $type = 'texte';
+
+	/** Le texte */
+	public string $texte;
+
+	/**
+	 * Contenu avant le texte.
+	 *
+	 * Vide ou apostrophe simple ou double si le texte en était entouré
+	 *
+	 * @var string|array
+	 */
+	public $avant = '';
+
+	/**
+	 * Contenu après le texte.
+	 *
+	 * Vide ou apostrophe simple ou double si le texte en était entouré
+	 *
+	 * @var string|array
+	 */
+	public $apres = '';
+
+	/** Numéro de ligne dans le code source du squelette */
+	public int $ligne = 0;
+}
-- 
GitLab