but-mmi-beziers / babel-moteur
Moteur de la Bibliothèque de Babel : permutation à clé entre adresses et pages de 3 200 caractères, avec intégration Laravel.
Package info
github.com/BUT-MMI-Beziers/moteur-bibliotheque-babel
pkg:composer/but-mmi-beziers/babel-moteur
Requires
- php: ^8.2
- ext-gmp: *
- ext-hash: *
- illuminate/console: ^12.0 || ^13.0
- illuminate/contracts: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
Requires (Dev)
- laravel/pint: ^1.20
- orchestra/testbench: ^10.0 || ^11.0
- phpstan/phpstan: ^2.1
- phpstan/phpstan-phpunit: ^2.0
- phpunit/phpunit: ^11.5.3 || ^12.0 || ^13.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-15 20:32:14 UTC
README
La bibliothèque imaginée par Borges contient toutes les pages possibles de 3 200 caractères, soit 29^3200 pages. Ce paquet est le moteur qui la fait exister sans rien stocker : vous lui donnez une adresse, il vous rend la page ; vous lui donnez un texte, il vous rend l'adresse d'une page qui le contient.
Vous l'installez dans votre projet Laravel et vous vous en servez comme d'une boîte noire : ce document suffit, inutile de lire le code.
Sommaire
- Ce que fait le moteur
- Prérequis : PHP et l'extension gmp
- Installation en 3 commandes
- Utilisation dans Laravel
- Les adresses
- Les modes de recherche
- Les erreurs
- La clé : règles d'or
- Ce que le moteur ne fait pas (c'est votre travail)
- Dépannage
1. Ce que fait le moteur
Trois méthodes, sur un objet Babel\Moteur\Moteur :
| Méthode | Fonction du cahier des charges | Renvoie |
|---|---|---|
lire(string $adresse): string |
F1 | le texte de la page à cette adresse (3 200 caractères) |
chercher(string $texte, string $mode): string |
F2 | l'adresse d'une page qui contient ce texte |
empreinte(): string |
F8 | l'empreinte de la clé (64 caractères hexadécimaux), à publier avec l'identité de votre bibliothèque |
Ce que le moteur vous garantit :
- Bijection exacte : deux adresses différentes donnent deux pages différentes, et toute page de 3 200 caractères a exactement une adresse.
chercher(lire($a), 'page') === $apour toute adresse valide. - Déterminisme : même clé, mêmes entrées → même résultat, aujourd'hui, dans un an, sur toutes les machines (y compris en mode
bruit). - Aucun état : pas de base de données, pas de fichier, pas de cache, pas de réseau. Le moteur ne fait que calculer (environ 1 à 2 ms par appel).
- La clé ne sort jamais : elle n'apparaît ni dans les résultats, ni dans les messages d'erreur, ni dans
dd().
Chaque bibliothèque (donc chaque étudiant) a sa propre clé, donc son propre rangement : la même adresse ne donne pas la même page chez vous et chez votre voisin.
2. Prérequis : PHP et l'extension gmp
| Version | |
|---|---|
| PHP | 8.2 ou plus (8.3 ou plus pour Laravel 13) |
| Laravel | 12 ou 13 |
| Extension PHP | gmp (calcul sur de très grands nombres) |
Vérifiez que gmp est active :
php -m | grep -i gmp
Si rien ne s'affiche, activez-la :
| Système | Comment faire |
|---|---|
| Ubuntu, Debian, WSL | sudo apt install php8.3-gmp (remplacez 8.3 par votre version de PHP, voir php -v) |
| macOS (Homebrew, Laravel Herd) | normalement déjà incluse ; sinon brew reinstall php |
| Windows (PHP seul, Laragon, XAMPP) | dans php.ini, retirez le ; devant extension=gmp, puis redémarrez le serveur |
Docker (image php officielle) |
apt-get install -y libgmp-dev && docker-php-ext-install gmp dans le Dockerfile |
⚠️ Le PHP de la ligne de commande et celui du serveur web peuvent être différents (
php-fpm, Apache). L'extension doit être active dans les deux. Sansgmp, Composer refuse d'installer le paquet et le dit :requires PHP extension ext-gmp.
3. Installation en 3 commandes
Dans le dossier de votre projet Laravel :
composer require but-mmi-beziers/babel-moteur
php artisan babel:cle
php artisan babel:verifier
composer requireinstalle le moteur. Laravel le découvre tout seul : rien à déclarer.php artisan babel:cletire une clé au hasard et ajoute une ligneBABEL_KEY=base64:…dans votre.env. Si une clé existe déjà, la commande refuse de l'écraser (voir § 8).php artisan babel:verifiercontrôle l'installation : extensiongmp, clé, résultats de référence, aller-retour avec votre clé. Elle affiche l'empreinte de votre clé et le temps moyen d'une lecture, et doit finir par « Tout est prêt ».
Moteur de la Bibliothèque de Babel (babel-moteur/v1)
Extension gmp ..................................... GMP 6.3.0 OK
Clé BABEL_KEY ......................... présente et bien formée OK
Vecteurs de référence (clés de test A et B) ............. 48/48 OK
Aller-retour avec la clé de la bibliothèque ..... lire ⇄ chercher OK
Empreinte de la clé ......... 17733b48109ec09205a57b1db673c70c64bba4c4641097caf62b697a39149473
Lecture moyenne .................................. 1.76 ms (sur 50 lectures)
INFO Tout est prêt : le moteur est utilisable.
Options de babel:cle :
| Commande | Effet |
|---|---|
php artisan babel:cle --afficher |
affiche la ligne BABEL_KEY=… sans toucher au .env (pour la copier ailleurs, par exemple sur le serveur de production) |
php artisan babel:cle --force |
remplace une clé existante. Toutes les adresses déjà partagées changeront de texte : à ne faire qu'avant l'ouverture de votre bibliothèque |
Facultatif : php artisan vendor:publish --tag=babel-config copie le fichier de configuration config/babel.php dans votre projet. Vous n'en avez normalement pas besoin.
4. Utilisation dans Laravel
Le moteur est enregistré dans le conteneur de Laravel : demandez-le en paramètre (injection de dépendances), ne faites jamais new Moteur(...) vous-même. Il n'y a pas de façade.
Dans un contrôleur
<?php namespace App\Http\Controllers; use Babel\Moteur\Erreur\ErreurMoteur; use Babel\Moteur\Moteur; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; class PageController extends Controller { public function __construct(private readonly Moteur $moteur) { } // GET /api/pages/{adresse} public function show(string $adresse): JsonResponse { try { return response()->json([ 'adresse' => $adresse, 'texte' => $this->moteur->lire($adresse), ]); } catch (ErreurMoteur $e) { // $e->versTableau() : [['code' => 'ERR_…', 'champ' => '…', 'message' => '…'], …] return response()->json(['erreurs' => $e->versTableau()], 422); } } // POST /api/recherches { "texte": "...", "mode": "exact" } public function search(Request $request): JsonResponse { try { $adresse = $this->moteur->chercher( (string) $request->input('texte', ''), (string) $request->input('mode', 'exact'), ); return response()->json(['adresse' => $adresse]); } catch (ErreurMoteur $e) { return response()->json(['erreurs' => $e->versTableau()], 422); } } }
Le statut HTTP (422, 400…) et la forme exacte du JSON sont ceux de votre contrat d'API : adaptez l'exemple.
Traiter toutes les erreurs du moteur au même endroit (facultatif)
Plutôt que des try/catch partout, vous pouvez traduire ErreurMoteur une fois pour toutes dans bootstrap/app.php :
use Babel\Moteur\Erreur\ErreurMoteur; use Illuminate\Foundation\Configuration\Exceptions; ->withExceptions(function (Exceptions $exceptions) { $exceptions->render(fn (ErreurMoteur $e) => response()->json(['erreurs' => $e->versTableau()], 422)); })
Ailleurs : services, commandes, tâches de fond
// Dans une méthode, n'importe où : $texte = app(\Babel\Moteur\Moteur::class)->lire('0-w1-s1-v1:1'); // Dans une tâche en file d'attente : injectez le moteur dans handle(), jamais dans le constructeur. public function handle(\Babel\Moteur\Moteur $moteur): void { $texte = $moteur->lire($this->adresse); }
Un Moteur refuse d'être sérialisé (serialize() lève une exception) : il ne doit pas voyager dans la charge d'une tâche. Il est immuable et réutilisable : il fonctionne tel quel sous Octane et dans les workers de file d'attente.
L'empreinte
$moteur->empreinte(); // "f85a8e8ae6a8a1ce…" : 64 caractères hexadécimaux minuscules
Elle identifie votre clé sans la révéler. Elle ne change jamais tant que la clé ne change pas. Publiez-la avec l'identité de votre bibliothèque.
5. Les adresses
Une adresse de page s'écrit :
<hexagone>-w<mur>-s<étagère>-v<volume>:<page>
| Partie | Valeurs | Écriture |
|---|---|---|
| hexagone | de 0 au dernier hexagone (un nombre de 3 004 chiffres) |
base 36 : chiffres 0-9 puis lettres minuscules a-z, sans zéro de tête |
| mur | 1 à 4 | décimal, sans zéro de tête ni signe |
| étagère | 1 à 5 | idem |
| volume | 1 à 32 | idem |
| page | 1 à 410 | idem |
Exemples valides : 0-w1-s1-v1:1 (la toute première page), babel-w2-s3-v17:204, 1x-w4-s5-v32:410.
- Une page n'a qu'une seule écriture valide : le moteur refuse
0-w04-s1-v1:1,ABC-w1-s1-v1:1ou0-w1-s1-v1:1(espace final). Il ne corrige rien, il ne fait pas detrim(). - Une adresse renvoyée par
chercher()peut faire jusqu'à 3 018 caractères. - Le dernier hexagone ne contient qu'une seule page :
<dernier hexagone>-w1-s1-v1:1.
Pour écrire vos propres fonctions sans recopier de nombres, utilisez les constantes publiques :
| Constante | Valeur |
|---|---|
Moteur::ALPHABET |
'abcdefghijklmnopqrstuvwxyz ,.' (29 symboles, dans cet ordre) |
Moteur::LONGUEUR_PAGE |
3200 |
Moteur::MURS, Moteur::ETAGERES, Moteur::VOLUMES, Moteur::PAGES |
4, 5, 32, 410 |
Moteur::LONGUEUR_MAX_HEXAGONE |
3004 |
Moteur::MODES |
['exact', 'bruit', 'page'] |
Moteur::ALGORITHME |
'babel-moteur/v1' |
6. Les modes de recherche
Le texte cherché ne contient que les 29 symboles de l'alphabet : les 26 lettres minuscules sans accent, l'espace, la virgule et le point. Pas de majuscule, pas d'apostrophe, pas de saut de ligne. C'est à vous de normaliser le texte avant (fonction F3).
| Mode | La page trouvée contient… | Usage |
|---|---|---|
exact |
votre texte au début, suivi d'espaces jusqu'à 3 200 caractères | retrouver « la » page de ce texte, propre et lisible |
bruit |
votre texte placé quelque part, entouré de caractères qui ont l'air aléatoires | une page « naturelle », comme si on était tombé dessus par hasard |
page |
exactement votre texte, qui doit faire 3 200 caractères | retrouver l'adresse d'une page complète (par exemple une page lue plus tôt) |
- Même texte, même mode, même clé → toujours la même adresse, y compris en mode
bruit(le « hasard » est dérivé de la clé et du texte). - Le moteur ne renvoie que l'adresse, pas la position du texte dans la page : retrouvez-la vous-même (fonction F4
localiser). - Un texte de 3 200 caractères donne la même adresse dans les trois modes.
7. Les erreurs
Une entrée refusée lève une Babel\Moteur\Erreur\ErreurMoteur. C'est un cas normal, que votre serveur traduit en réponse HTTP.
try { $moteur->lire('ABC-w9-s1-v1:1'); } catch (ErreurMoteur $e) { $e->getMessage(); // "ERR_INVALID_HEX_CHARS (hexagone), ERR_OUT_OF_BOUNDS (mur)" $e->erreurs(); // liste d'objets Erreur : ->code (enum CodeErreur), ->champ, ->message $e->versTableau(); // [['code' => 'ERR_INVALID_HEX_CHARS', 'champ' => 'hexagone', 'message' => '…'], [...]] }
lirerenvoie toutes les erreurs de l'adresse d'un coup, dans l'ordre hexagone, mur, etagere, volume, page (au plus une par partie).cherchers'arrête à la première erreur : une seule à la fois.- Les messages sont en français et ne recopient jamais votre entrée.
| Code | Champ(s) | Quand |
|---|---|---|
ERR_INVALID_ADDRESS_SYNTAX |
adresse |
l'adresse n'a pas la forme …-w…-s…-v…:… |
hexagone |
hexagone vide, ou zéro de tête (01) |
|
mur, etagere, volume, page |
vide, zéro de tête, signe, point, chiffre non ASCII ; ou :page absente |
|
ERR_INVALID_HEX_CHARS |
hexagone |
caractère hors de 0-9a-z (majuscule, espace, accent…) |
ERR_OUT_OF_BOUNDS |
hexagone |
au-delà du dernier hexagone |
mur, etagere, volume, page |
hors de 1–4, 1–5, 1–32, 1–410 (0 compris) |
|
adresse |
page inexistante dans le dernier hexagone | |
ERR_UNKNOWN_MODE |
mode |
mode autre que exact, bruit, page (la casse compte) |
ERR_EMPTY_QUERY |
texte |
texte vide |
ERR_INVALID_ALPHABET |
texte |
caractère hors de l'alphabet |
ERR_INVALID_PAGE_LENGTH |
texte |
mode page et texte qui ne fait pas 3 200 caractères |
ERR_TEXT_TOO_LONG |
texte |
modes exact et bruit, texte de plus de 3 200 caractères |
Une autre exception, Babel\Moteur\Erreur\CleInvalide, signale une clé absente ou mal formée. Ce n'est pas une erreur de l'utilisateur mais d'installation : lancez php artisan babel:cle puis php artisan babel:verifier. Ne l'attrapez pas pour la transformer en 422 : laissez-la remonter en 500, elle doit se voir.
8. La clé : règles d'or
La clé fixe le rangement de votre bibliothèque. Elle vit dans .env :
BABEL_KEY=base64:…44 caractères…
- Ne la changez jamais une fois des adresses partagées : toutes vos adresses désigneraient un autre texte. (C'est pour ça que
babel:clerefuse d'écraser une clé existante sans--force.) - Ne la committez jamais et ne la mettez jamais dans le code :
.envest déjà ignoré par Git dans un projet Laravel. - Sauvegardez-la (gestionnaire de mots de passe) : perdue, votre bibliothèque est perdue.
- En production, copiez la même ligne
BABEL_KEY=…dans le.envdu serveur (ne relancez pasbabel:clelà-bas). - Elle n'a rien à voir avec
APP_KEY: ne la dérivez pas, ne les mélangez pas. - Lisez-la avec
config('babel.cle')si besoin, jamaisenv('BABEL_KEY')dans votre code : aprèsphp artisan config:cache,env()renvoienull. (En pratique, vous n'avez jamais besoin de la lire : injectezMoteur.) - Seule l'empreinte se publie.
9. Ce que le moteur ne fait pas (c'est votre travail)
| Fonction | Où | Indice |
|---|---|---|
F3 normaliser(texte) |
votre serveur | minuscules, accents retirés, caractères hors alphabet remplacés ou supprimés. Le moteur refuse un texte non normalisé, il ne le convertit pas |
F5 valider(adresse) |
votre serveur | les règles du § 5 et du tableau des erreurs ; les constantes Moteur::MURS… vous évitent de recopier les nombres |
F7 auHasard() |
votre serveur | tirez une adresse valide au hasard (attention : l'hexagone est un nombre de 3 004 chiffres en base 36) |
F4 localiser, F6 voisine |
votre application web | position du texte dans la page, page suivante ou précédente |
| Routes, statuts HTTP, JSON, liens courts, marque-pages… | votre serveur |
10. Dépannage
| Symptôme | Cause et solution |
|---|---|
requires PHP extension ext-gmp * but it is missing |
l'extension gmp n'est pas active : voir § 2 |
Call to undefined function gmp_… en navigateur mais pas en ligne de commande |
gmp est active pour la ligne de commande seulement : activez-la aussi pour le PHP du serveur web |
CleInvalide : La clé BABEL_KEY est absente |
pas de ligne BABEL_KEY dans .env : php artisan babel:cle |
CleInvalide : … n'est pas un base64 valide |
la clé a été mal recopiée (espace, caractère en moins) : recopiez la ligne entière |
La clé est dans .env mais le moteur dit qu'elle est absente |
la configuration est en cache : php artisan config:clear (ou relancez php artisan config:cache) |
| Mes adresses ne donnent plus les mêmes pages | la clé a changé (--force, autre .env, autre machine). Comparez l'empreinte affichée par php artisan babel:verifier |
ErreurMoteur sur une adresse qui « a l'air bonne » |
regardez $e->versTableau() : zéro de tête, majuscule, espace en trop, page absente… |
| Un texte avec des accents est refusé | normal : normalisez-le d'abord (F3) |
Algorithme babel-moteur/v1 : réseau de Feistel à 8 tours sur HMAC-SHA256, avec cycle-walking. Cette construction convient à un projet pédagogique ; ce n'est pas un chiffrement normalisé, ne la réutilisez pas pour protéger des données réelles.
Licence MIT.