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.
At a Glance
Section titled “At a Glance”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 EventsQuick Comparison
Section titled “Quick Comparison”| Feature | XOOPS 2.7.x | XOOPS 4.0 |
|---|---|---|
| PHP Version | 8.2+ | 8.4+ |
| Request Handling | Page Controller (index.php) | Router + Middleware |
| Dependencies | Global objects ($xoopsDB) | PSR-11 Container injection |
| Events | Preload system | PSR-14 Event Dispatcher |
| Templates | Smarty 4 | Smarty 4/5 |
| Autoloading | Class maps | PSR-4 native |
| Module Metadata | xoops_version.php | xoops_version.php + module.json |
| HTTP Messages | Superglobals | PSR-7 Request/Response |
PSR Standards Adopted
Section titled “PSR Standards Adopted”PSR-4: Autoloading
Section titled “PSR-4: Autoloading”What: Standard class autoloading based on namespace/directory mapping.
// Before: Manual includes or class mapsrequire_once XOOPS_ROOT_PATH . '/modules/mymod/class/Item.php';
// After: PSR-4 autoloadinguse XoopsModules\MyModule\Item; // Automatically loadedPSR-7: HTTP Messages
Section titled “PSR-7: HTTP Messages”What: Standardized request/response objects instead of superglobals.
// Before: Direct superglobal access$id = $_GET['id'];echo '<html>...</html>';
// After: PSR-7 objectspublic function handle(ServerRequestInterface $request): ResponseInterface{ $id = $request->getQueryParams()['id']; return new HtmlResponse($this->render('template.tpl'));}PSR-11: Dependency Injection Container
Section titled “PSR-11: Dependency Injection Container”What: Centralized service container for managing dependencies.
// Before: Global accessglobal $xoopsDB;$handler = xoops_getModuleHandler('item', 'mymod');
// After: Container injectionpublic function __construct( private readonly ItemRepositoryInterface $items, private readonly LoggerInterface $logger) {}PSR-14: Event Dispatcher
Section titled “PSR-14: Event Dispatcher”What: Typed event objects with subscriber priority and propagation control.
// Before: Preload with array argumentspublic 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();}PSR-15: HTTP Middleware
Section titled “PSR-15: HTTP Middleware”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:#c8e6c9New Module Features
Section titled “New Module Features”module.json Manifest
Section titled “module.json Manifest”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 } }}Route-Based Dispatching
Section titled “Route-Based Dispatching”// Routes in module.json"routes": { "articles.show": { "path": "/articles/{id}", "controller": "ArticleController::show", "methods": ["GET"] }}
// Clean controllerclass ArticleController{ public function show(int $id, ArticleService $service): ResponseInterface { $article = $service->find($id); return $this->render('article/show.tpl', ['article' => $article]); }}CLI Commands
Section titled “CLI Commands”Register commands in module.json:
"commands": { "mymod:import": { "class": "MyModule\\Command\\ImportCommand", "description": "Import data from CSV" }}Run with:
php xoops mymod:import --file=data.csvREST API Support
Section titled “REST API Support”Built-in REST API routing:
"api": { "prefix": "/api/v1/mymodule", "routes": { "articles.list": { "path": "/articles", "controller": "Api\\ArticleController::list", "methods": ["GET"] } }}Hybrid Mode Compatibility
Section titled “Hybrid Mode Compatibility”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| Level | What Works | Migration Effort |
|---|---|---|
| H0 | Unmodified legacy modules | None required |
| H1 | Legacy with deprecation logging | 1-2 hours |
| H2 | Mix of legacy and modern | 1-4 days |
| H3 | Fully modern (no legacy) | 1-2 weeks |
Migration Checklist
Section titled “Migration Checklist”Minimum Required (H0 → H1)
Section titled “Minimum Required (H0 → H1)”- Test module on PHP 8.4
- Add
module.jsonwith basic metadata - Run hybrid compatibility test suite
- Review deprecation log
Recommended (H1 → H2)
Section titled “Recommended (H1 → H2)”- Wrap handlers in Repository classes
- Create Service layer for business logic
- Replace
global $xoopsDBwith injection - Add unit tests for services
- Register optional routes in
module.json
Full Modernization (H2 → H3)
Section titled “Full Modernization (H2 → H3)”- Convert all pages to routes
- Remove all global dependencies
- Migrate preloads to PSR-14 events
- Update templates to Smarty 4
- Register CLI commands
Breaking Changes
Section titled “Breaking Changes”⚠️ Removed in XOOPS 4.0
Section titled “⚠️ Removed in XOOPS 4.0”| Feature | Replacement |
|---|---|
{php} template blocks | Smarty functions/modifiers |
Direct $_GET/$_POST | ServerRequestInterface |
global $xoopsDB (in H3) | Container injection |
XoopsObjectTree | Nested Set / Closure Table |
⚠️ Deprecated (works, will be removed)
Section titled “⚠️ Deprecated (works, will be removed)”| Feature | Replacement | Removal Target |
|---|---|---|
xoops_getModuleHandler() | Container service | XOOPS 3.0 |
$GLOBALS['xoopsUser'] | UserContext service | XOOPS 3.0 |
| Preload events | PSR-14 subscribers | XOOPS 3.0 |
What Stays the Same
Section titled “What Stays the Same”These APIs remain stable and unchanged:
✅ XoopsObject — getVar(), 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
Learning Resources
Section titled “Learning Resources”| Resource | Description |
|---|---|
| Vision 2026 Module | Reference implementation |
| 4.0 Tutorial | Step-by-step new module |
| Pattern Decision Tree | Choose the right approach |
| Event Decision Tree | Preloads vs PSR-14 |
| Documentation Map | Navigate all d |