Developer Reference
API documentation for building VaultBB premium extensions on phpBB 3.3.x.
Developer Reference
This page documents everything you need to build a VaultBB premium extension for phpBB 3.3.x. All services described here are for premium extensions only — free/standalone phpBB extensions must never depend on VaultBB Core.
Available Services
VaultBB Core registers seven Symfony DI services, all injectable via config/services.yml:
| Service ID | Class | Purpose |
|---|---|---|
vaultbb.core.registry | vaultbb\core\registry\registry | Extension registry — read/write |
vaultbb.core.logger | vaultbb\core\logger\logger | Unified audit logger |
vaultbb.core.admin_ui | vaultbb\core\admin_ui\admin_ui | ACP component library |
vaultbb.core.error_handler | vaultbb\core\error\error_handler | Production-safe exception management |
vaultbb.core.settings | vaultbb\core\settings\settings | Per-extension key-value configuration store |
vaultbb.core.notifier | vaultbb\core\notification\notifier | Persistent ACP notification queue |
vaultbb.core.update_checker | vaultbb\core\update\update_checker | Remote update manifest checker |
Declaring Core as a Dependency
composer.json
"require": {
"php": ">=7.4.0",
"phpbb/phpbb": ">=3.3.0,<3.4.0",
"vaultbb/core": ">=1.0.0"
}ext.php — is_enableable()
Every VaultBB extension must block activation if Core is absent or too old:
public function is_enableable(): bool|\phpbb\extension\exception
{
if (!$this->container->has('vaultbb.core.registry')) {
return new \phpbb\extension\exception(
'VAULTBB_CORE_REQUIRED',
['My Extension', '1.0.0']
);
}
$registry = $this->container->get('vaultbb.core.registry');
if (!$registry->meetsVersion('1.0.0')) {
return new \phpbb\extension\exception(
'VAULTBB_CORE_VERSION_REQUIRED',
['My Extension', '1.0.0']
);
}
return true;
}ext.php — Registry lifecycle
// enable_step() — after all migrations have run
$registry->register([
'ext_name' => 'vaultbb/myextension',
'title' => 'My Extension',
'version' => '1.0.0',
'core_min' => '1.0.0',
'active' => true,
'meta' => ['features' => ['feature_a']],
]);
// disable_step()
$registry->setActive('vaultbb/myextension', false);
// purge_step()
$registry->unregister('vaultbb/myextension');Missing any step of the Registry lifecycle will cause the Dashboard to show stale data. All three — register on enable, setActive(false) on disable, unregister on purge — are mandatory.
Registry (vaultbb.core.registry)
Class: vaultbb\core\registry\registry
Writing
// Register a new extension (called in enable_step)
$registry->register([
'ext_name' => 'vaultbb/myextension',
'title' => 'My Extension',
'version' => '1.0.0',
'core_min' => '1.0.0',
'active' => true,
'meta' => [],
]);
// Mark as inactive (called in disable_step)
$registry->setActive('vaultbb/myextension', false);
// Remove entirely (called in purge_step)
$registry->unregister('vaultbb/myextension');Reading
// Get a single extension record (returns null if not found)
$info = $registry->get('vaultbb/myextension');
// Keys: ext_name, ext_title, ext_version, core_min, ext_active,
// ext_meta, registered_at, updated_at
// Get all registered extensions (array keyed by ext_name, cached)
$all = $registry->getAll();
// Get the version string ('0.0.0' if not found)
$version = $registry->getVersion('vaultbb/myextension');
// Check active state
$active = $registry->isActive('vaultbb/myextension'); // bool
// Check if the installed Core meets a minimum version requirement
$ok = $registry->meetsVersion('1.0.0'); // boolLogger (vaultbb.core.logger)
Class: vaultbb\core\logger\logger
Writing logs
$this->logger->log(
'myextension', // source — your extension's short codename
'item_created', // action — see standard names below
[ // context — any key/value pairs useful for debugging
'item_id' => $item_id,
'user_id' => $user_id,
]
);Use consistent action names across all VaultBB extensions:
| Action | When to use |
|---|---|
ext_enabled | Extension enabled |
ext_disabled | Extension disabled |
settings_updated | ACP settings form submitted |
item_created | A record was created |
item_updated | A record was updated |
item_deleted | A record was deleted |
feature_used | A significant user-facing feature ran |
error_occurred | An exception was caught (auto-logged by ErrorHandler) |
permission_denied | An authorization check failed |
cache_cleared | A cache was manually busted |
Reading logs
// Fetch recent logs for this extension (paginated)
$logs = $this->logger->getLogs(['source' => 'myextension'], 30, 0);
// Count total entries matching a filter (for pagination)
$total = $this->logger->countLogs(['source' => 'myextension']);
// Delete entries older than N days, returns the count of deleted rows
$deleted = $this->logger->purgeLogs(90);AdminUI (vaultbb.core.admin_ui)
Class: vaultbb\core\admin_ui\admin_ui
All VaultBB ACP modules must use AdminUI exclusively. No bespoke admin styling is permitted.
Brand header
Call once at the top of every ACP mode. Sets S_VAULTBB_BRAND_HEADER, VAULTBB_BRAND_TITLE, and VAULTBB_BRAND_SUBTITLE in the template.
$this->admin_ui->brand_header(
$this->user->lang('ACP_MYEXTENSION'),
$this->user->lang('ACP_MYEXTENSION_EXPLAIN')
);Metrics
$this->admin_ui->metric('Total Items', (string) $count, '#2f6fed');
$this->admin_ui->metric('Active Today', (string) $today, '#16a34a');
$this->admin_ui->metric('Errors', (string) $errors,'#dc2626');The template iterates {% for metric in vaultbb_metrics %} to render tiles.
Status rows
$this->admin_ui->status_row('Feature X', (bool) $this->config['myext_feature_x']);
$this->admin_ui->status_row('Cache', true, 'Warm', 'Cold');The template iterates {% for row in vaultbb_status_rows %}.
Alerts
$this->admin_ui->alert('Settings saved.', 'success');
$this->admin_ui->alert('Something looks off.', 'warning');Available types: success · info · warning · danger
Inline badge
// Returns an HTML string — use |raw in Twig
$badge = $this->admin_ui->badge(true, 'Active', 'Inactive');ACP template structure
Your .html templates must include the VaultBB base stylesheet and use the provided CSS class names:
<!-- INCLUDE @vaultbb_core/vaultbb_core_base.html -->
<div class="vaultbb-acp-wrap">
{% if S_VAULTBB_BRAND_HEADER %}
<div class="vaultbb-brand-header">
<div class="vaultbb-brand-logo">V</div>
<div class="vaultbb-brand-text">
<p class="vaultbb-brand-title">{{ VAULTBB_BRAND_TITLE }}</p>
<p class="vaultbb-brand-subtitle">{{ VAULTBB_BRAND_SUBTITLE }}</p>
</div>
<span class="vaultbb-brand-version">v{{ VAULTBB_CORE_VERSION }}</span>
</div>
{% endif %}
<!-- your content here -->
</div>Error Handler (vaultbb.core.error_handler)
Class: vaultbb\core\error\error_handler
Handling a caught exception
try {
$this->doSomethingRisky();
} catch (\Throwable $e) {
$this->error_handler->handle($e, 'myextension', 'save_item');
// Production: exception is logged and swallowed — forum stays online
// When vaultbb_core_enabled is off: exception is re-thrown for inspection
}Safe callable wrapper
$result = $this->error_handler->safe(
fn() => $this->riskyOperation(),
'myextension',
[] // default value returned if the callable throws
);Settings Store (vaultbb.core.settings)
Class: vaultbb\core\settings\settings
A per-extension key-value store backed by a dedicated database table (not phpBB's phpbb_config). Use this for extension-specific values that are not global phpBB config.
// Write a value (creates or updates)
$this->settings->set('myextension', 'some_key', 'some_value');
// Read a value (returns null if not set)
$value = $this->settings->get('myextension', 'some_key');
// Delete a value
$this->settings->delete('myextension', 'some_key');Notifier (vaultbb.core.notifier)
Class: vaultbb\core\notification\notifier
Posts persistent notifications to the VaultBB Dashboard. Useful for extension warnings that should remain visible until an admin actively dismisses them.
// Post a notification (survives page loads until dismissed)
$this->notifier->notify(
'myextension',
'warning', // type: 'info' | 'warning' | 'error'
'LANG_KEY_OR_RAW_MSG'
);
// Dismiss a notification by ID (called automatically by the Dashboard UI)
$this->notifier->dismiss($notification_id);ACP Module Structure
All VaultBB ACP modules must live under the ACP_VAULTBB tab created by Core. Never create a separate top-level tab.
In your migration's update_data():
// Core has already created ACP_VAULTBB — add your category under it
['module.add', ['acp', 'ACP_VAULTBB', 'ACP_VAULTBB_MYEXTENSION']],
['module.add', ['acp', 'ACP_VAULTBB_MYEXTENSION', [
'module_basename' => '\vaultbb\myextension\acp\main_module',
'modes' => ['overview', 'settings'],
]]],Cross-Extension Communication
Use the Registry to safely check whether another VaultBB extension is active before interacting with it:
$registry = $this->container->get('vaultbb.core.registry');
if ($registry->isActive('vaultbb/otherextension')) {
$other = $this->container->get('vaultbb.otherextension.some_service');
$other->doSomething();
}Dispatching Custom Events
Every VaultBB extension must dispatch named phpBB events at key moments so third-party code can hook into it:
$event = new \phpbb\event\data([
'item_id' => $item_id,
'item_data' => $item_data, // writable — listeners may modify this
'user_id' => $user_id, // read-only — document this clearly
]);
$this->dispatcher->dispatch($event, 'vaultbb.myextension.item_created');
// Always read data back after dispatch
$item_data = $event['item_data'];Document all dispatched events in your extension's docs/EVENTS.md.
Template Variables Provided by Core
VaultBB Core assigns the following Twig variables on every front-end and ACP page load (via core.page_header and core.adm_page_header):
| Variable | Type | Description |
|---|---|---|
S_VAULTBB_CORE_ACTIVE | bool | true when Core is enabled |
VAULTBB_CORE_VERSION | string | Installed Core version string |
Use these in any VaultBB template to conditionally render Core-powered features:
{% if S_VAULTBB_CORE_ACTIVE %}
{# render something that requires Core #}
{% endif %}