Skip to content

Hybrid Mode Compatibility Contract

Purpose: This document defines the formal compatibility guarantees for running legacy XOOPS modules alongside modern XOOPS 4.0 components.


Hybrid Mode is the cornerstone of XOOPS 4.0’s migration strategy. It ensures that:

  1. Existing modules continue to work without modification
  2. Gradual migration is possible page-by-page, module-by-module
  3. New and legacy code can coexist in the same installation
  4. No ecosystem fragmentation occurs during the transition

XOOPS 4.0 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

Unmodified legacy modules run without any changes.

AspectGuarantee
Entry Pointsmodules/<mod>/index.php, modules/<mod>/admin/*.php
Globals$xoopsDB, $xoopsUser, $xoopsModule, $xoopsTpl available
HandlersClassic XoopsPersistableObjectHandler works
TemplatesSmarty 3 syntax fully supported
BlocksLegacy block functions and templates work
Metadataxoops_version.php is authoritative

Legacy modules run with logging and wrapper adapters enabled.

AspectGuarantee
All H0 features✅ Supported
Deprecation WarningsLogged (not displayed) when legacy APIs used
Request WrapperLegacy $_GET/$_POST access wrapped for PSR-7
Database WrapperLegacy $xoopsDB calls forwarded to new connection
Template WrapperSmarty 3 calls adapted to Smarty 4

Modules can opt into new services while using legacy entry points where needed.

AspectGuarantee
Container AccessPSR-11 container available via Xoops::getContainer()
Event SystemCan subscribe to PSR-14 events
RouterCan register routes in module.json
Service LayerCan inject services into controllers
Legacy BridgeLegacy globals still accessible (but discouraged)
Mixed PagesSome pages can use new stack, others legacy

Modules use only new stack APIs; legacy globals unavailable by default.

AspectGuarantee
ContainerRequired for all dependencies
RouterAll entry points via router
MiddlewareFull PSR-15 pipeline
TemplatesSmarty 4 only, no {php} blocks
EventsPSR-14 event dispatcher
CLICommands registered via manifest

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
TransitionEstimated EffortComplexityRecommended For
H0 → H11-2 hoursLowAll modules (safety net)
H1 → H21-4 daysMediumActive development modules
H2 → H31-2 weeksHighNew modules, major rewrites

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

Entry PointH0H1H2H3
modules/<mod>/index.php
modules/<mod>/admin/*.php
Route-based (/mod/action)
Block rendering
CLI commands

The following initialization order is guaranteed:

  1. Config Loadmainfile.php or environment config
  2. Error Handler — Error/exception handling registered
  3. Container Build — DI container compiled (H2+)
  4. Session Start — Session initialized
  5. User Load$xoopsUser available
  6. Module Load — Current module context set
  7. Template Ready$xoopsTpl initialized
GlobalWhen AvailableH0H1H2H3
$xoopsDBAfter bootstrap⚠️
$xoopsUserAfter session❌*
$xoopsTplAfter template init⚠️
$xoopsModuleIn module context❌*
$xoopsConfigAfter bootstrap❌*

*Available via container injection in H3


These APIs will not change without a major version bump:

  • XoopsObject::getVar(), setVar(), toArray()
  • XoopsPersistableObjectHandler CRUD methods
  • Criteria and CriteriaCompo classes
  • xoops_getModuleHandler() function
  • Block rendering pipeline
  • Permission check APIs
  • CSRF token utilities

These APIs work but will be removed in a future major version:

APIReplacementRemoval Target
global $xoopsDBContainer injectionXOOPS 3.0
$_REQUEST direct accessRequest::get()XOOPS 3.0
{php} in templatesTwig functions / modifiersXOOPS 3.0
XoopsObjectTreeNested Set or Closure TableXOOPS 3.0

These APIs are in preview and may change:

  • JSON module manifests (module.json)
  • CLI command registration
  • Middleware stack configuration
  • Event subscriber attributes

LevelMax Additional Overhead
H00 ms (baseline)
H1< 5 ms
H2< 15 ms
H3Container-dependent
FeatureGuarantee
Template CachingWorks in all levels
Block CachingUnchanged behavior
OPcacheFully compatible
Query CachingOpt-in, same API
  • Disable existing caching
  • Require per-request filesystem scans
  • Add N+1 query overhead via wrappers
  • Force module recompilation on every request

PatternNotes
Preloads/HooksBridged to new event system
Classic handlers + CriteriaWorks unchanged
Block definitions in xoops_version.phpFully supported
Template overrides in themesWorks as before
Language files (language/)Loaded automatically
Admin menu registrationWorks unchanged
PatternConstraint
Direct global usageAllowed in H0/H1, discouraged in H2, disabled in H3
Custom Smarty pluginsMust register via new mechanism in H2+
Direct SQL queriesWork, but should migrate to repository

These patterns are intentionally blocked for security:

PatternReason
Dynamic file includes from user inputSecurity risk
{php} blocks in templatesXSS/RCE risk
eval() on user dataRCE risk
Unescaped outputXSS risk

  1. Page-by-page migration: Convert one admin page or one frontend action at a time
  2. Independent adoption: Adopt the container without adopting the router (or vice versa)
  3. No big-bang required: Legacy and modern can coexist indefinitely

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 4.0


The following tests verify Hybrid Mode compliance:

TestDescription
LegacyModuleBootTestLegacy demo module loads and renders
Vision2026ModuleTestModern reference module works
MixedModuleTestHalf legacy/half modern module works
ThemeOverrideTestTemplate overrides work in hybrid
BlockCacheTestBlock caching works across levels
PermissionTestPermission checks work in all levels
  • All core PRs must pass Hybrid Compliance Suite
  • Module developers can run: php xoops test:hybrid mymodule

The following guarantees will NEVER be broken without a new major version:

  1. xoops_version.php remains authoritative module metadata
  2. ✅ Legacy entry points (modules/<mod>/*.php) work via bridge
  3. ✅ Block system works with legacy functions and templates
  4. ✅ Legacy globals available in H0/H1 levels
  5. ✅ Theme template override resolution unchanged
  6. ✅ Permission system API unchanged


Version History:

VersionDateChanges
1.0.02026-01-31Initial draft

#hybrid-mode #compatibility #migration #specification #xoops-4.0