Skip to content

What's New in XOOPS 4.0

One-page summary of everything new in XOOPS 4.0 — PSR standards, architecture changes, new APIs, and migration requirements.


mindmap
root((XOOPS 4.0))
PSR Standards
PSR-4 Autoloading
PSR-7 HTTP Messages
PSR-11 DI Container
PSR-14 Events
PSR-15 Middleware
Architecture
Router-based Dispatch
Dependency Injection
Middleware Pipeline
Smarty 4
Compatibility
Hybrid Mode H0-H3
Legacy Bridge
Gradual Migration
New Features
REST API Support
CLI Commands
module.json Manifests
Typed Events

FeatureXOOPS 2.7.xXOOPS 4.0
PHP Version8.2+8.4+
Request HandlingPage Controller (index.php)Router + Middleware
DependenciesGlobal objects ($xoopsDB)PSR-11 Container injection
EventsPreload systemPSR-14 Event Dispatcher
TemplatesSmarty 4Smarty 4/5
AutoloadingClass mapsPSR-4 native
Module Metadataxoops_version.phpxoops_version.php + module.json
HTTP MessagesSuperglobalsPSR-7 Request/Response

What: Standard class autoloading based on namespace/directory mapping.

// Before: Manual includes or class maps
require_once XOOPS_ROOT_PATH . '/modules/mymod/class/Item.php';
// After: PSR-4 autoloading
use XoopsModules\MyModule\Item; // Automatically loaded

📖 Full PSR-4 Guide


What: Standardized request/response objects instead of superglobals.

// Before: Direct superglobal access
$id = $_GET['id'];
echo '<html>...</html>';
// After: PSR-7 objects
public function handle(ServerRequestInterface $request): ResponseInterface
{
$id = $request->getQueryParams()['id'];
return new HtmlResponse($this->render('template.tpl'));
}

📖 Full PSR-7 Guide


What: Centralized service container for managing dependencies.

// Before: Global access
global $xoopsDB;
$handler = xoops_getModuleHandler('item', 'mymod');
// After: Container injection
public function __construct(
private readonly ItemRepositoryInterface $items,
private readonly LoggerInterface $logger
) {}

📖 Full PSR-11 Guide


What: Typed event objects with subscriber priority and propagation control.

// Before: Preload with array arguments
public static function eventCoreUserLogin(array $args): void {
$user = $args[0];
}
// After: Typed event objects
#[AsEventListener(event: UserLoginEvent::class, priority: 10)]
public function onLogin(UserLoginEvent $event): void {
$user = $event->getUser();
}

📖 Full Event System Guide


What: Composable request/response processing pipeline.

flowchart LR
REQ[Request] --> M1[Auth Middleware]
M1 --> M2[CSRF Middleware]
M2 --> M3[Module Router]
M3 --> CTRL[Controller]
CTRL --> M3
M3 --> M2
M2 --> M1
M1 --> RES[Response]
style REQ fill:#e3f2fd
style RES fill:#c8e6c9

📖 Full PSR-15 Guide


New JSON-based module metadata alongside xoops_version.php:

{
"name": "mymodule",
"version": "1.0.0",
"xoops": {
"min": "4.0",
"compatibility": "H2"
},
"routes": {
"mymodule.index": {
"path": "/mymodule",
"controller": "IndexController"
}
},
"services": {
"MyModule\\Service\\ItemService": {
"autowire": true
}
}
}

📖 Module JSON Spec


// Routes in module.json
"routes": {
"articles.show": {
"path": "/articles/{id}",
"controller": "ArticleController::show",
"methods": ["GET"]
}
}
// Clean controller
class ArticleController
{
public function show(int $id, ArticleService $service): ResponseInterface
{
$article = $service->find($id);
return $this->render('article/show.tpl', ['article' => $article]);
}
}

Register commands in module.json:

"commands": {
"mymod:import": {
"class": "MyModule\\Command\\ImportCommand",
"description": "Import data from CSV"
}
}

Run with:

Terminal window
php xoops mymod:import --file=data.csv

Built-in REST API routing:

"api": {
"prefix": "/api/v1/mymodule",
"routes": {
"articles.list": {
"path": "/articles",
"controller": "Api\\ArticleController::list",
"methods": ["GET"]
}
}
}

📖 REST API Guide


XOOPS 4.0 maintains backward compatibility through Hybrid Mode:

flowchart LR
H0["🔴 H0<br/>Pure Legacy"] --> H1["🟡 H1<br/>Legacy + Shims"]
H1 --> H2["🟢 H2<br/>Hybrid"]
H2 --> H3["🔵 H3<br/>Modern Only"]
style H0 fill:#ffcdd2
style H1 fill:#fff9c4
style H2 fill:#c8e6c9
style H3 fill:#bbdefb
LevelWhat WorksMigration Effort
H0Unmodified legacy modulesNone required
H1Legacy with deprecation logging1-2 hours
H2Mix of legacy and modern1-4 days
H3Fully modern (no legacy)1-2 weeks

📖 Full Hybrid Mode Contract


  • Test module on PHP 8.4
  • Add module.json with basic metadata
  • Run hybrid compatibility test suite
  • Review deprecation log
  • Wrap handlers in Repository classes
  • Create Service layer for business logic
  • Replace global $xoopsDB with injection
  • Add unit tests for services
  • Register optional routes in module.json
  • Convert all pages to routes
  • Remove all global dependencies
  • Migrate preloads to PSR-14 events
  • Update templates to Smarty 4
  • Register CLI commands

📖 Complete Migration Guide


FeatureReplacement
{php} template blocksSmarty functions/modifiers
Direct $_GET/$_POSTServerRequestInterface
global $xoopsDB (in H3)Container injection
XoopsObjectTreeNested Set / Closure Table

⚠️ Deprecated (works, will be removed)

Section titled “⚠️ Deprecated (works, will be removed)”
FeatureReplacementRemoval Target
xoops_getModuleHandler()Container serviceXOOPS 3.0
$GLOBALS['xoopsUser']UserContext serviceXOOPS 3.0
Preload eventsPSR-14 subscribersXOOPS 3.0

These APIs remain stable and unchanged:

XoopsObjectgetVar(), setVar(), toArray()XoopsPersistableObjectHandler — CRUD methods ✅ Criteria and CriteriaCompo — Query building ✅ Block system — Functions and templates ✅ Permission checking — Group/module permissions ✅ Theme template overrides — Resolution unchanged ✅ xoops_version.php — Still authoritative


ResourceDescription
Vision 2026 ModuleReference implementation
4.0 TutorialStep-by-step new module
Pattern Decision TreeChoose the right approach
Event Decision TreePreloads vs PSR-14
Documentation MapNavigate all d