Zum Inhalt springen
Nibiru docsv0.9.2

Module

Erstellen von Nibiru-Modulen — das zweite M in MMVC. Merkmale, Plugins, Schnittstellen, Einstellungen, Beobachter.

Stable Reading time ~ 4 min Edit on GitHub

Ein Modul ist eine selbstständige Domänen-Einheit. Es besitzt seine Konfiguration, seine Plugins (Dienste), seine Traits (wiederverwendbare Methoden), seine Schnittstellen und optional einen eigenen MVC-Slice. Module sind die Art und Weise, wie Nibiru das fat-controller-Problem ohne einen Dienstcontainer vermeidet.

Botanische Anatomie eines Nibiru-Moduls, als geschichtete Blätter einer Lotus-Zehe gezeichnet — äußere Blätter, mittlere Blätter, innere Blätter, Kelchschicht, Empfänger.
Ein Modul, Schicht für Schicht.
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>.php

Der Registry durchsucht application/module/ beim Starten, entdeckt die jeweiligen Module in settings/*.ini, analysiert den Abschnitt, der mit dem Modulnamen (in Großbuchstaben) übereinstimmt, und speichert ihn für den Abruf über Registry::getInstance()->loadModuleConfigByName('users').

<?php
namespace 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);
}
}

Die IModule Schnittstelle ist absichtlich ein Marker – der tatsächliche Verhalten befindet sich vollständig in Ihren Traits und Plugins.

Ein Plugin ist eine Klasse, die Controller instanziieren können, um auf Modulfunktionen zuzugreifen:

application/module/users/plugins/user.php
<?php
namespace 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';
}
}

In einem Controller:

$this->user = new \Nibiru\Module\Users\Plugin\User();
if (!$this->user->isAuthorized()) {
View::forwardTo('/login');
}

Plugins erben von der Modulklasse und teilen daher Zugriff auf den Registrierungsmechanismus, die Einstellungen und das Beobachtergerüst.

Ein Trait trägt wiederverwendbare Methodenkörper, die die Modulklassen benötigen. Häufiges Muster: Form-Factories.

application/module/users/traits/users.php
<?php
namespace 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',
]);
}
}

Die Hauptklasse Users bringt es über use Traits\Users; ein.

Jeder Modul kann seine eigenen INI-Dateien haben. Der Registrierungsmechanismus analysiert jede *.ini-Datei im Verzeichnis settings/ und sucht nach einem Abschnitt, der den Namen des Moduls (in Großbuchstaben) trägt:

; application/module/users/settings/users.ini
[USERS]
session.lifetime = 7200
password.min.length = 12
allowed.roles[] = "admin"
allowed.roles[] = "editor"
allowed.roles[] = "standard"

Lesen Sie es von überall aus:

$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$cfg->session_lifetime; / 7200
$cfg->password_min_length; / 12
$cfg->allowed_roles; / [admin, editor, standard]

Umgebungsüberschreibungen: Eine Datei mit dem Namen users.production.ini wird vorzugsweise gegenüber users.ini verwendet, wenn APPLICATION_ENV=production.

Module, die SplSubject implementieren, können Ereignisse an angehängte Beobachter übertragen, ohne mit ihnen gekoppelt zu sein. Aus der Präsentation meldet das Modul analytics jedem angehängten Tracker bei jeder Seitenansicht:

// 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()

Jeder Beobachter erhält die Analytics-Instanz und zieht die Ereignisdaten aus, über die er sich interessiert.

Von den Showcase-Anwendungen:

  • auth (TPMS) — Sitzungsverwaltung, QR-Code-Anmeldung, rollenbasierte Zugriffskontrolle. Implementiert SplSubject, sodass Anmelde-/Abmeldeereignisse an Protokollierungs- und Überwachungsmodule weitergegeben werden können.
  • cms (prod.maschinen-stockert.de) — Inhaltsverwaltung, indiziert durch Controller-Pfad + Sprache. Ermöglicht es Nicht-Entwicklern, die Seitenkopien zu aktualisieren.
  • graph_mail (TPMS) — Microsoft Graph API-Wrapper für transaktionale E-Mails.
  • pdfgenerator (prod.maschinen-stockert.de) — Generiert Maschinenkataloge aus DB-gesteuerten Vorlagen.
  • machineryscout — Elasticsearch-Indexverwaltung mit Merkmalen für Indizierung, Abfrage und erneute Indizierung.
  • assetmanager — zentrales CSS/JS-Pipeline, verwendet zum Wechseln von Themen je nach Sprache.
  • analytics — beobachtungsgetriebene Tracker-Ausbreitung (Matomo usw.).

Modulregistrierung: Wie das Framework Ihr Modul findet

Abschnitt betitelt „Modulregistrierung: Wie das Framework Ihr Modul findet“

Ein Modulordner auf der Festplatte ist erforderlich, aber nicht ausreichend. Das Framework muss auch wissen, wie es geladen wird – das geschieht mit drei Positionierungsarrays in Ihrer [AUTOLOADER]-Konfiguration:

; 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.php
class.pos[] = "billing"
class.plugin.pos[] = "" ; reserved

Die Namen sind kleinbuchstabeigeordnete Ordnername, genau wie sie unter application/module/ erscheinen. Der Frameworks Auto::loader() (aufgerufen vom Dispatcher) durchläuft jede Modul in der Reihenfolge und erfordert seine Dateien.

Pluginklassen liegen im Plural-Namespace Plugins:

application/module/billing/plugins/invoice.php
namespace Nibiru\Module\Billing\Plugins; / plural
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }

Namespace und Klassenname nicht übereinstimmen und es treten Autoloader-Fehlschläge auf. Der CLI-Scaffold-Befehl (./nibiru -m billing) generiert den richtigen Namespace für Sie.

Sie registrieren Ihre Modul-INI-Dateien nicht — der Registry entdeckt sie automatisch, indem er application/module/<name>/settings/*.ini durchläuft, nachdem [AUTOLOADER] die Modulklassen lädt. Jede Sektion [<MODULE>] (in Großbuchstaben) in einer INI wird zur Verfügung gestellt als:

$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
$cfg->invoice_prefix; / [BILLING] invoice.prefix property

Der Registrierungsmodul bevorzugt <Modul>.<Umgebung>.ini (z.B. billing.production.ini), wenn APPLICATION_ENV übereinstimmt.

Wenn Ihr Modul Datenbanktabellen hat, fügen Sie SQL-Dateien in application/settings/config/database/ ein, die nummeriert sind und sich auf den bestehenden Bereich beziehen. Konvention des AI-Moduls:

200-ai_rag_collection.sql
201-ai_rag_chunk.sql
202-ai_conversation.sql
203-ai_message.sql

Führen Sie mit ./nibiru -mi local aus. Das Framework generiert automatisch Modelle in application/model/<table>.php, wenn [GENERATOR] database = true gesetzt ist, bereit zur Verwendung über \Nibiru\Pdo::fetchAll(…).

Terminal-Fenster
./nibiru -m billing

Skelette:

application/module/billing/
├── billing.php
├── interfaces/billing.php
├── plugins/
├── settings/billing.ini
└── traits/

Fügen Sie den Schalter -g (./nibiru -m billing -g) hinzu, wenn Sie Graylog-Protokollierungshooks vordefiniert in die Struktur einfügen möchten.

Verwenden Sie nicht jede Controller-Klasse als Modul. Der Schwellenwert ist: haben Sie mindestens ein Trait, eine Erweiterung und einen INI-Schlüssel? Wenn ja, verdient es sein eigenes Modulverzeichnis. Wenn nein, reicht ein freigegebener Trait-Datei oder eine kleine Hilfsdatei in application/lib/.