Modules
Construire des modules Nibiru — le deuxième M dans MMVC. Caractéristiques, plug-ins, interfaces, paramètres, observateurs.
Un module est une unité de domaine autonome. Il possède sa propre configuration, ses plugins (services), ses traits (méthodes réutilisables), ses interfaces et éventuellement une tranche MVC de son propre. Les modules sont la façon dont Nibiru évite le problème du fat-controller sans un conteneur de services.
Anatomie
Section intitulée « Anatomie »application/module/<name>/├── <name>.php # main class (implements IModule, optionally SplSubject)├── interfaces/ # contracts for plugins / external consumers│ └── <name>.php├── plugins/ # stateless services usable from controllers│ ├── <thing>.php│ └── <other>.php├── settings/ # auto-discovered .ini files│ ├── <name>.ini│ └── <name>.production.ini└── traits/ # reusable method groups └── <name>.phpLe Registry parcourt application/module/ au démarrage, découvre chaque fichier settings/*.ini du module, analyse la section correspondant au nom du module (en majuscules) et le met en cache pour une recherche via Registry::getInstance()->loadModuleConfigByName('users').
Un module minimal
Section intitulée « Un module minimal »<?phpnamespace Nibiru\Module\Users;
use Nibiru\Module as ModuleAdapter;use Nibiru\Interfaces\IModule;use Nibiru\Registry;
class Users extends ModuleAdapter implements IModule, \SplSubject{ use Traits\Users;
const CONFIG_MODULE_NAME = 'users';
protected static \stdClass $usersRegistry; protected \SplObjectStorage $observers;
public function __construct() { $this->setUsersRegistry(); $this->observers = new \SplObjectStorage(); }
public function attach(\SplObserver $o): void { $this->observers->attach($o); } public function detach(\SplObserver $o): void { $this->observers->detach($o); } public function notify(): void { foreach ($this->observers as $o) { $o->update($this); } }
protected function setUsersRegistry(): void { self::$usersRegistry = Registry::getInstance() ->loadModuleConfigByName(self::CONFIG_MODULE_NAME); }}L’interface IModule est délibérément un marqueur — le comportement réel se trouve dans vos traits et vos plugins.
Plugins : services sans état
Section intitulée « Plugins : services sans état »Un plugin est une classe que les contrôleurs peuvent instancier pour accéder aux fonctionnalités du module :
<?phpnamespace Nibiru\Module\Users\Plugin;
use Nibiru\Module\Users\Users;use Nibiru\Pdo;
class User extends Users{ public function isAuthorized(): bool { return isset($_SESSION['auth']['user_id']); }
public function checkForStandardUser(): bool { return $this->isAuthorized() && ($_SESSION['auth']['role'] ?? '') === 'standard'; }}Dans un contrôleur :
$this->user = new \Nibiru\Module\Users\Plugin\User();if (!$this->user->isAuthorized()) { View::forwardTo('/login');}Les plugins héritent de la classe du module, donc ils partagent accès au registre, aux paramètres et à la machine observateur.
Caractéristiques : méthodes réutilisables
Section intitulée « Caractéristiques : méthodes réutilisables »Un trait contient des corps de méthodes réutilisables que la classe du module souhaite. Modèle courant : usines de formulaires.
<?phpnamespace Nibiru\Module\Users\Traits;
use Nibiru\Form;
trait Users{ public function loginForm(): string { Form::create(); Form::addOpenDiv(['class' => 'form-group']); Form::addInputTypeText([ 'class' => 'form-control', 'name' => 'login', 'placeholder' => 'Username', ]); Form::addCloseDiv(); Form::addOpenDiv(['class' => 'form-group']); Form::addInputTypePassword([ 'class' => 'form-control', 'name' => 'password', 'placeholder' => 'Password', ]); Form::addCloseDiv(); return Form::addForm([ 'method' => 'POST', 'action' => '/login', 'name' => 'loginForm', ]); }}La classe principale Users l’intègre via use Traits\Users;.
Paramètres du module (INI)
Section intitulée « Paramètres du module (INI) »Chaque module peut contenir ses propres fichiers INI. Le Registre analyse chaque fichier *.ini dans le dossier settings/ et recherche une section nommée d’après le module (en majuscules) :
; application/module/users/settings/users.ini[USERS]session.lifetime = 7200password.min.length = 12allowed.roles[] = "admin"allowed.roles[] = "editor"allowed.roles[] = "standard"Lisez-le à partir d’ailleurs :
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');$cfg->session_lifetime; / 7200$cfg->password_min_length; / 12$cfg->allowed_roles; / [admin, editor, standard]Superpositions d’environnement : un fichier nommé users.production.ini est préféré par rapport à users.ini lorsque APPLICATION_ENV=production.
Le modèle observateur
Section intitulée « Le modèle observateur »Les modules implémentant SplSubject peuvent diffuser des événements aux observateurs attachés sans être couplés à eux. À partir de l’exemple, le module analytics informe tout suivi attaché à chaque affichage de page :
// in a controller$analytics = new \Nibiru\Module\Analytics\Analytics();$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
$analytics->trackPageView(); / internally calls notify()Chaque observateur reçoit l’instance d’analytics via sa méthode update($subject) et extrait les données d’événement dont il a besoin.
Modules de production réels
Section intitulée « Modules de production réels »Depuis les applications de démonstration :
auth(TPMS) — gestion des sessions, connexion par code QR, accès basé sur les rôles. ImplémenteSplSubjectafin que les événements de connexion/déconnexion puissent être diffusés aux modules d’enregistrement et de conformité.cms(prod.maschinen-stockert.de) — magasin de contenu cléé par chemin du contrôleur + langue. Permet aux non-développeurs de mettre à jour le contenu du site.graph_mail(TPMS) — enveloppe de l’API Microsoft Graph pour les e-mails transactionnels.pdfgenerator(prod.maschinen-stockert.de) — génère des catalogues de machines à partir de modèles pilotés par la base de données.machineryscout— gestion de l’index Elasticsearch avec des traits pour l’indexation, la requête et le re-indexage.assetmanager— pipeline CSS/JS centralisé, utilisé pour basculer les thèmes selon la langue.analytics— suivi basé sur les observateurs diffusant (Matomo, etc.).
Enregistrement du module : comment le framework trouve votre module
Section intitulée « Enregistrement du module : comment le framework trouve votre module »Un dossier de module sur le disque est nécessaire mais non suffisant. Le framework doit également savoir le charger — c’est fait avec trois tableaux de positions dans votre configuration [AUTOLOADER] :
; application/settings/config/settings.development.ini
[AUTOLOADER]iface.pos[] = "users" ; load application/module/users/interfaces/iface.pos[] = "billing" ; load application/module/billing/interfaces/trait.pos[] = "users" ; load application/module/users/traits/trait.pos[] = "billing"class.pos[] = "users" ; load application/module/users/users.phpclass.pos[] = "billing"class.plugin.pos[] = "" ; reservedLes noms sont des noms de dossiers en minuscules, exactement tels qu’ils apparaissent sous application/module/. Le chargeur automatique du framework (Auto::loader()) (appelé par le Dispatcher) parcourt chaque module dans l’ordre, en chargeant ses fichiers.
Convention d’espace de noms pour les plugins
Section intitulée « Convention d’espace de noms pour les plugins »Les classes de plugin se trouvent dans l’espace de noms pluriel Plugins:
namespace Nibiru\Module\Billing\Plugins; / ← pluralclass Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }Ne correspondez pas à l’espace de noms et vous obtiendrez des manquements d’autoload. Le squelette CLI (./nibiru -m billing) génère l’espace de noms correct pour vous.
Découverte des paramètres
Section intitulée « Découverte des paramètres »Vous ne vous inscrivez pas les fichiers INI de votre module — le Registry les découvre automatiquement en parcourant application/module/<name>/settings/*.ini après que [AUTOLOADER] a chargé la classe du module. Chaque section [<MODULE>] (en majuscules) d’un INI devient disponible comme :
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');$cfg->invoice_prefix; / [BILLING] invoice.prefix → propertyLe Registre préfère <module>.<env>.ini (par exemple billing.production.ini) lorsque APPLICATION_ENV correspond.
Migrations
Section intitulée « Migrations »Si votre module a des tables de base de données, déposez les fichiers SQL dans application/settings/config/database/, numérotés après la plage existante. La convention utilisée par le module IA :
200-ai_rag_collection.sql201-ai_rag_chunk.sql202-ai_conversation.sql203-ai_message.sqlExécutez avec ./nibiru -mi local. Le framework génère automatiquement des modèles dans application/model/<table>.php lorsque [GENERATOR] database = true est défini, prêts à l’utilisation via \Nibiru\Pdo::fetchAll(…).
Génération d’un module
Section intitulée « Génération d’un module »./nibiru -m billingÉchafaudages :
application/module/billing/├── billing.php├── interfaces/billing.php├── plugins/├── settings/billing.ini└── traits/Ajoutez l’option -g (./nibiru -m billing -g) lorsque vous souhaitez que les accroches de journalisation Graylog soient pré-connectées dans le squelette.
Quand ne pas créer un module
Section intitulée « Quand ne pas créer un module »Ne promouvez pas chaque contrôleur en un module. Le seuil est : avez-vous au moins un trait, une extension et une clé INI ? Si oui, il mérite son propre dossier de module. Sinon, un fichier de traits partagés ou un petit assistant dans application/lib/ suffit amplement.