Gå til indhold

XOOPS arkitektur

Dette dokument giver et omfattende overblik over XOOPS systemarkitekturen og forklarer, hvordan de forskellige komponenter arbejder sammen for at skabe et fleksibelt og udvidbart indholdsstyringssystem.

XOOPS følger en modulær arkitektur, der adskiller bekymringer i forskellige lag. Systemet er bygget op omkring flere kerneprincipper:

  • Modularitet: Funktionalitet er organiseret i uafhængige, installerbare moduler
  • Udvidelsesmuligheder: Systemet kan udvides uden at ændre kernekoden
  • Abstraktion: Database- og præsentationslag er abstraheret fra forretningslogik
  • Sikkerhed: Indbyggede sikkerhedsmekanismer beskytter mod almindelige sårbarheder
graph TB
subgraph Presentation["🎨 Presentation Layer"]
Themes["Themes"]
Templates["Smarty Templates"]
Blocks["Blocks"]
end
subgraph Application["⚙️ Application Layer"]
Modules["Modules"]
Preloads["Preloads"]
Controllers["Controllers"]
BlockHandlers["Block Handlers"]
end
subgraph Domain["📦 Domain Layer"]
XoopsObject["XoopsObject"]
Handlers["Object Handlers"]
Criteria["Criteria System"]
end
subgraph Infrastructure["🔧 Infrastructure Layer"]
Database["XoopsDatabase"]
Cache["Cache System"]
Session["Session Manager"]
Security["Security Layer"]
end
Presentation --> Application
Application --> Domain
Domain --> Infrastructure
style Presentation fill:#e8f5e9,stroke:#388e3c
style Application fill:#e3f2fd,stroke:#1976d2
style Domain fill:#fff3e0,stroke:#f57c00
style Infrastructure fill:#fce4ec,stroke:#c2185b

Præsentationslaget håndterer brugergrænsefladegengivelse ved hjælp af Smarty-skabelonmotoren.

Nøglekomponenter:

  • Temaer: Visuel styling og layout
  • Smarty skabeloner: Dynamisk indholdsgengivelse
  • Blokkeringer: Genanvendelige indholdswidgets

Applikationslaget indeholder forretningslogik, controllere og modulfunktionalitet.

Nøglekomponenter:

  • Moduler: Selvstændige funktionspakker
  • Behandlere: Datamanipulationsklasser
  • Preloads: Eventlyttere og hooks

Domænelaget indeholder kerneforretningsobjekter og regler.

Nøglekomponenter:

  • XoopsObject: Basisklasse for alle domæneobjekter
  • Behandlere: CRUD operationer for domæneobjekter

Infrastrukturlaget leverer kernetjenester som databaseadgang og caching.

At forstå anmodningens livscyklus er afgørende for effektiv udvikling af XOOPS.

Den nuværende XOOPS 2.5.x bruger et Page Controller-mønster, hvor hver PHP-fil håndterer sin egen anmodning. Globaler ($xoopsDB, $xoopsUser, $xoopsTpl osv.) initialiseres under bootstrap og er tilgængelige under udførelse.

sequenceDiagram
participant Browser
participant Entry as modules/mymod/index.php
participant Main as mainfile.php
participant Kernel as XOOPS Kernel
participant DB as $xoopsDB
participant User as $xoopsUser
participant Handler as MyObjectHandler
participant Tpl as $xoopsTpl (Smarty)
participant Theme
Browser->>Entry: GET /modules/mymod/index.php
rect rgb(240, 248, 255)
Note over Entry,User: Bootstrap Phase (mainfile.php)
Entry->>Main: include mainfile.php
Main->>Kernel: Initialize Core
Kernel->>DB: Create XoopsDatabase (singleton)
Kernel->>User: Load Session → $xoopsUser
Kernel->>Tpl: Initialize Smarty → $xoopsTpl
Main-->>Entry: Globals Ready
end
rect rgb(255, 250, 240)
Note over Entry,Handler: Page Controller Execution
Entry->>Handler: xoops_getModuleHandler('myobject')
Handler->>DB: query via Criteria
DB-->>Handler: Result Set
Handler-->>Entry: XoopsObject[]
end
rect rgb(240, 255, 240)
Note over Entry,Theme: Rendering Phase
Entry->>Tpl: $xoopsTpl->assign('items', $objects)
Entry->>Theme: include header.php
Entry->>Tpl: $xoopsTpl->display('mymod_index.tpl')
Entry->>Theme: include footer.php
Theme-->>Browser: Complete HTML Page
end
GlobalSkrivInitialiseretFormål
$xoopsDBXoopsDatabaseBootstrapDatabaseforbindelse (singleton)
$xoopsUserXoopsUser|nullSessionsbelastningAktuel logget ind bruger
$xoopsTplXoopsTplSkabelon initSmart skabelonmotor
$xoopsModuleXoopsModuleModulbelastningAktuel modulkontekst
$xoopsConfigarrayKonfig indlæsSystemkonfiguration
// mainfile.php is the entry point
include_once XOOPS_ROOT_PATH . '/mainfile.php';
// Core initialization
$xoops = Xoops::getInstance();
$xoops->boot();

Trin:

  1. Indlæs konfiguration (mainfile.php)
  2. Initialiser autoloader
  3. Opsæt fejlhåndtering
  4. Opret databaseforbindelse
  5. Indlæs brugersession
  6. Initialiser Smarty skabelonmotor
// Request routing to appropriate module
$module = $GLOBALS['xoopsModule'];
$controller = $module->getController();
$controller->dispatch($request);

Trin:

  1. Parse anmodning URL
  2. Identificer målmodulet
  3. Indlæs modulkonfiguration
  4. Tjek tilladelser
  5. Rute til passende fører
// Controller execution
$data = $handler->getObjects($criteria);
$xoopsTpl->assign('items', $data);

Trin:

  1. Udfør controllerlogik
  2. Interager med datalaget
  3. Behandle forretningsregler
  4. Forbered visningsdata
// Template rendering
include XOOPS_ROOT_PATH . '/header.php';
$xoopsTpl->display('db:module_template.tpl');
include XOOPS_ROOT_PATH . '/footer.php';

Trin:

  1. Anvend temalayout
  2. Gengiv modulskabelon
  3. Procesblokke
  4. Output respons

Basisklassen for alle dataobjekter i XOOPS.

<?php
class MyModuleItem extends XoopsObject
{
public function __construct()
{
$this->initVar('id', XOBJ_DTYPE_INT, null, false);
$this->initVar('title', XOBJ_DTYPE_TXTBOX, '', true, 255);
$this->initVar('content', XOBJ_DTYPE_TXTAREA, '', false);
$this->initVar('created', XOBJ_DTYPE_INT, time(), false);
}
}

Nøglemetoder:

  • initVar() - Definer objektegenskaber
  • getVar() - Hent egenskabsværdier
  • setVar() - Indstil egenskabsværdier
  • assignVars() - Massetildeling fra array

Håndterer CRUD-operationer for XoopsObject-forekomster.

<?php
class MyModuleItemHandler extends XoopsPersistableObjectHandler
{
public function __construct(\XoopsDatabase $db)
{
parent::__construct($db, 'mymodule_items', 'MyModuleItem', 'id', 'title');
}
public function getActiveItems($limit = 10)
{
$criteria = new CriteriaCompo();
$criteria->add(new Criteria('status', 1));
$criteria->setSort('created');
$criteria->setOrder('DESC');
$criteria->setLimit($limit);
return $this->getObjects($criteria);
}
}

Nøglemetoder:

  • create() - Opret ny objektinstans
  • get() - Hent objekt efter ID
  • insert() - Gem objekt i databasen
  • delete() - Fjern objekt fra databasen
  • getObjects() - Hent flere objekter
  • getCount() - Tæl matchende objekter

Hvert XOOPS-modul følger en standard mappestruktur:

modules/mymodule/
├── class/ # PHP classes
│ ├── MyModuleItem.php
│ └── MyModuleItemHandler.php
├── include/ # Include files
│ ├── common.php
│ └── functions.php
├── templates/ # Smarty templates
│ ├── mymodule_index.tpl
│ └── mymodule_item.tpl
├── admin/ # Admin area
│ ├── index.php
│ └── menu.php
├── language/ # Translations
│ └── english/
│ ├── main.php
│ └── modinfo.php
├── sql/ # Database schema
│ └── mysql.sql
├── xoops_version.php # Module info
├── index.php # Module entry
└── header.php # Module header

Moderne XOOPS-udvikling kan udnytte afhængighedsinjektion til bedre testbarhed.

<?php
class XoopsDependencyContainer
{
private array $services = [];
public function register(string $name, callable $factory): void
{
$this->services[$name] = $factory;
}
public function resolve(string $name): mixed
{
if (!isset($this->services[$name])) {
throw new \InvalidArgumentException("Service not found: $name");
}
$factory = $this->services[$name];
if (is_callable($factory)) {
return $factory($this);
}
return $factory;
}
public function has(string $name): bool
{
return isset($this->services[$name]);
}
}
<?php
namespace Xmf\Di;
use Psr\Container\ContainerInterface;
class BasicContainer implements ContainerInterface
{
protected array $definitions = [];
public function set(string $id, mixed $value): void
{
$this->definitions[$id] = $value;
}
public function get(string $id): mixed
{
if (!$this->has($id)) {
throw new \InvalidArgumentException("Service not found: $id");
}
$entry = $this->definitions[$id];
if (is_callable($entry)) {
return $entry($this);
}
return $entry;
}
public function has(string $id): bool
{
return isset($this->definitions[$id]);
}
}
<?php
// Service registration
$container = new XoopsDependencyContainer();
$container->register('database', function () {
return XoopsDatabaseFactory::getDatabaseConnection();
});
$container->register('userHandler', function ($c) {
return new XoopsUserHandler($c->resolve('database'));
});
// Service resolution
$userHandler = $container->resolve('userHandler');
$user = $userHandler->get($userId);

XOOPS giver flere forlængelsesmekanismer:

Preloads gør det muligt for moduler at tilslutte sig kernebegivenheder.

modules/mymodule/preloads/core.php
<?php
class MymoduleCorePreload extends XoopsPreloadItem
{
public static function eventCoreHeaderEnd($args)
{
// Execute when header processing ends
}
public static function eventCoreFooterStart($args)
{
// Execute when footer processing starts
}
}

Plugins udvider specifik funktionalitet inden for moduler.

modules/mymodule/plugins/notify.php
<?php
class MymoduleNotifyPlugin
{
public function onItemCreate($item)
{
// Send notification when item is created
}
}

Filtre ændrer data, når de passerer gennem systemet.

<?php
// Content filter example
$myts = MyTextSanitizer::getInstance();
$content = $myts->displayTarea($rawContent, 1, 1, 1);
  1. Brug navnerum til ny kode:
navneområde XoopsModules\MyModule;
klasse Vare udvider \XoopsObject
{
// Implementering
}
  1. Følg PSR-4 autoloading:
{
"autoload": {
"psr-4": {
"XoopsModules\\MyModule\\": "klasse/"
}
}
}
  1. Særskilte bekymringer:
    • Domænelogik i class/
    • Præsentation i templates/
    • Controllere i modulrod
  1. Brug cache til dyre operationer
  2. Doven indlæs ressourcer, når det er muligt
  3. Minimer databaseforespørgsler ved hjælp af kriterie-batching
  4. Optimer skabeloner ved at undgå kompleks logik
  1. Valider alle input ved hjælp af Xmf\Request
  2. Escape output i skabeloner
  3. Brug forberedte udsagn til databaseforespørgsler
  4. Tjek tilladelser før følsomme handlinger

#xoops #arkitektur #kerne #design #system-design