Hybrid Mode Compatibility Contract (HCC)¶
Purpose: This document defines the formal compatibility guarantees for running legacy XOOPS modules alongside modern XOOPS 2026 components.
Overview¶
Hybrid Mode is the cornerstone of XOOPS 2026's migration strategy. It ensures that:
- Existing modules continue to work without modification
- Gradual migration is possible page-by-page, module-by-module
- New and legacy code can coexist in the same installation
- No ecosystem fragmentation occurs during the transition
Compatibility Levels¶
XOOPS 2026 defines four compatibility levels, allowing module authors to choose their migration pace:
graph LR
H0[H0: Legacy] --> H1[H1: Legacy + Shims]
H1 --> H2[H2: Hybrid]
H2 --> H3[H3: Modern]
style H0 fill:#ffcdd2
style H1 fill:#fff9c4
style H2 fill:#c8e6c9
style H3 fill:#bbdefb H0 — Pure Legacy¶
Unmodified legacy modules run without any changes.
| Aspect | Guarantee |
|---|---|
| Entry Points | modules/<mod>/index.php, modules/<mod>/admin/*.php |
| Globals | $xoopsDB, $xoopsUser, $xoopsModule, $xoopsTpl available |
| Handlers | Classic XoopsPersistableObjectHandler works |
| Templates | Smarty 3 syntax fully supported |
| Blocks | Legacy block functions and templates work |
| Metadata | xoops_version.php is authoritative |
H1 — Legacy with Compatibility Shims¶
Legacy modules run with logging and wrapper adapters enabled.
| Aspect | Guarantee |
|---|---|
| All H0 features | ✅ Supported |
| Deprecation Warnings | Logged (not displayed) when legacy APIs used |
| Request Wrapper | Legacy $_GET/$_POST access wrapped for PSR-7 |
| Database Wrapper | Legacy $xoopsDB calls forwarded to new connection |
| Template Wrapper | Smarty 3 calls adapted to Smarty 4 |
H2 — Hybrid Mode¶
Modules can opt into new services while using legacy entry points where needed.
| Aspect | Guarantee |
|---|---|
| Container Access | PSR-11 container available via Xoops::getContainer() |
| Event System | Can subscribe to PSR-14 events |
| Router | Can register routes in module.json |
| Service Layer | Can inject services into controllers |
| Legacy Bridge | Legacy globals still accessible (but discouraged) |
| Mixed Pages | Some pages can use new stack, others legacy |
H3 — Modern Only¶
Modules use only new stack APIs; legacy globals unavailable by default.
| Aspect | Guarantee |
|---|---|
| Container | Required for all dependencies |
| Router | All entry points via router |
| Middleware | Full PSR-15 pipeline |
| Templates | Smarty 4 only, no {php} blocks |
| Events | PSR-14 event dispatcher |
| CLI | Commands registered via manifest |
Migration Path Visualization¶
This diagram shows the progressive steps to migrate from H0 (legacy) to H3 (modern):
flowchart TB
subgraph H0["🔴 H0: Pure Legacy"]
H0A["✅ Runs unmodified"]
H0B["Uses globals directly"]
H0C["xoops_version.php only"]
H0D["Page Controller pattern"]
end
subgraph H1["🟡 H1: Legacy + Shims"]
H1A["Add module.json metadata"]
H1B["Enable deprecation logging"]
H1C["Run hybrid test suite"]
H1D["Review deprecation report"]
end
subgraph H2["🟢 H2: Hybrid"]
H2A["Wrap handlers in Repository"]
H2B["Create Service Layer"]
H2C["Register routes (optional)"]
H2D["Inject via Container"]
H2E["Subscribe to PSR-14 events"]
H2F["Add unit tests"]
end
subgraph H3["🔵 H3: Modern"]
H3A["Remove legacy entry points"]
H3B["All routes via router"]
H3C["Container-only dependencies"]
H3D["Smarty 4 templates"]
H3E["Full PSR-15 middleware"]
H3F["CLI commands registered"]
end
H0 -->|"Step 1: Test compatibility"| H1
H1 -->|"Step 2: Incremental refactor"| H2
H2 -->|"Step 3: Complete modernization"| H3
style H0 fill:#ffcdd2,stroke:#c62828
style H1 fill:#fff9c4,stroke:#f9a825
style H2 fill:#c8e6c9,stroke:#2e7d32
style H3 fill:#bbdefb,stroke:#1565c0 Migration Effort by Level¶
| Transition | Estimated Effort | Complexity | Recommended For |
|---|---|---|---|
| H0 → H1 | 1-2 hours | Low | All modules (safety net) |
| H1 → H2 | 1-4 days | Medium | Active development modules |
| H2 → H3 | 1-2 weeks | High | New modules, major rewrites |
Incremental Adoption Strategy¶
You don't have to go from H0 to H3 all at once. Here's a recommended approach:
flowchart LR
subgraph Phase1["Phase 1: Safety (H0→H1)"]
P1A["1. Add module.json"]
P1B["2. Enable shim logging"]
P1C["3. Fix critical deprecations"]
end
subgraph Phase2["Phase 2: Foundation (H1→H2)"]
P2A["4. Create Repository for one handler"]
P2B["5. Add Service for one feature"]
P2C["6. Write tests for Service"]
P2D["7. Repeat for other handlers"]
end
subgraph Phase3["Phase 3: Modernize (H2→H3)"]
P3A["8. Convert pages to routes"]
P3B["9. Remove global dependencies"]
P3C["10. Adopt PSR-15 middleware"]
end
Phase1 --> Phase2 --> Phase3 Execution Guarantees¶
Supported Entry Points¶
| Entry Point | H0 | H1 | H2 | H3 |
|---|---|---|---|---|
modules/<mod>/index.php | ✅ | ✅ | ✅ | ❌ |
modules/<mod>/admin/*.php | ✅ | ✅ | ✅ | ❌ |
Route-based (/mod/action) | ❌ | ❌ | ✅ | ✅ |
| Block rendering | ✅ | ✅ | ✅ | ✅ |
| CLI commands | ❌ | ❌ | ✅ | ✅ |
Bootstrap Order¶
The following initialization order is guaranteed:
- Config Load —
mainfile.phpor environment config - Error Handler — Error/exception handling registered
- Container Build — DI container compiled (H2+)
- Session Start — Session initialized
- User Load —
$xoopsUseravailable - Module Load — Current module context set
- Template Ready —
$xoopsTplinitialized
Global Availability¶
| Global | When Available | H0 | H1 | H2 | H3 |
|---|---|---|---|---|---|
$xoopsDB | After bootstrap | ✅ | ✅ | ⚠️ | ❌ |
$xoopsUser | After session | ✅ | ✅ | ✅ | ❌* |
$xoopsTpl | After template init | ✅ | ✅ | ⚠️ | ❌ |
$xoopsModule | In module context | ✅ | ✅ | ✅ | ❌* |
$xoopsConfig | After bootstrap | ✅ | ✅ | ✅ | ❌* |
*Available via container injection in H3
API Stability Promises¶
Stable (No Breaking Changes)¶
These APIs will not change without a major version bump:
XoopsObject::getVar(),setVar(),toArray()XoopsPersistableObjectHandlerCRUD methodsCriteriaandCriteriaCompoclassesxoops_getModuleHandler()function- Block rendering pipeline
- Permission check APIs
- CSRF token utilities
Deprecated (Works, Will Be Removed)¶
These APIs work but will be removed in a future major version:
| API | Replacement | Removal Target |
|---|---|---|
global $xoopsDB | Container injection | XOOPS 3.0 |
$_REQUEST direct access | Request::get() | XOOPS 3.0 |
{php} in templates | Twig functions / modifiers | XOOPS 3.0 |
XoopsObjectTree | Nested Set or Closure Table | XOOPS 3.0 |
Experimental (May Change)¶
These APIs are in preview and may change:
- JSON module manifests (
module.json) - CLI command registration
- Middleware stack configuration
- Event subscriber attributes
Performance Guarantees¶
Boot Overhead Budget¶
| Level | Max Additional Overhead |
|---|---|
| H0 | 0 ms (baseline) |
| H1 | < 5 ms |
| H2 | < 15 ms |
| H3 | Container-dependent |
Caching Behavior¶
| Feature | Guarantee |
|---|---|
| Template Caching | Works in all levels |
| Block Caching | Unchanged behavior |
| OPcache | Fully compatible |
| Query Caching | Opt-in, same API |
What Hybrid Mode Will NOT Do¶
- Disable existing caching
- Require per-request filesystem scans
- Add N+1 query overhead via wrappers
- Force module recompilation on every request
Supported Legacy Patterns¶
✅ Fully Supported¶
| Pattern | Notes |
|---|---|
| Preloads/Hooks | Bridged to new event system |
| Classic handlers + Criteria | Works unchanged |
Block definitions in xoops_version.php | Fully supported |
| Template overrides in themes | Works as before |
Language files (language/) | Loaded automatically |
| Admin menu registration | Works unchanged |
⚠️ Supported with Constraints¶
| Pattern | Constraint |
|---|---|
| Direct global usage | Allowed in H0/H1, discouraged in H2, disabled in H3 |
| Custom Smarty plugins | Must register via new mechanism in H2+ |
| Direct SQL queries | Work, but should migrate to repository |
❌ Not Supported (Security)¶
These patterns are intentionally blocked for security:
| Pattern | Reason |
|---|---|
| Dynamic file includes from user input | Security risk |
{php} blocks in templates | XSS/RCE risk |
eval() on user data | RCE risk |
| Unescaped output | XSS risk |
Migration Promises¶
Incremental Migration¶
- Page-by-page migration: Convert one admin page or one frontend action at a time
- Independent adoption: Adopt the container without adopting the router (or vice versa)
- No big-bang required: Legacy and modern can coexist indefinitely
Migration Cookbook¶
The following conversions are documented with examples:
- Convert one admin page to controller
- Convert one block to modern rendering
- Wrap handler in repository pattern
- Add service layer to existing module
- Introduce unit tests for handler
- Migrate from Smarty 3 to Smarty 4 syntax
See: Migration Guide: 2.5.x to 2026
Compliance Testing¶
Test Suite¶
The following tests verify Hybrid Mode compliance:
| Test | Description |
|---|---|
LegacyModuleBootTest | Legacy demo module loads and renders |
GoldStandardModuleTest | Modern reference module works |
MixedModuleTest | Half legacy/half modern module works |
ThemeOverrideTest | Template overrides work in hybrid |
BlockCacheTest | Block caching works across levels |
PermissionTest | Permission checks work in all levels |
CI Integration¶
- All core PRs must pass Hybrid Compliance Suite
- Module developers can run:
php xoops test:hybrid mymodule
Red Lines (Non-Negotiables)¶
The following guarantees will NEVER be broken without a new major version:
- ✅
xoops_version.phpremains authoritative module metadata - ✅ Legacy entry points (
modules/<mod>/*.php) work via bridge - ✅ Block system works with legacy functions and templates
- ✅ Legacy globals available in H0/H1 levels
- ✅ Theme template override resolution unchanged
- ✅ Permission system API unchanged
Related Documents¶
Version History:
| Version | Date | Changes |
|---|---|---|
| 1.0.0 | 2026-01-31 | Initial draft |