API Reference
Complete API reference for Health Checker extension development.
Core Interfaces
HealthCheckInterface
The main interface that all health checks must implement.
interface HealthCheckInterface
{
/**
* Get unique slug identifier (e.g., "core.php_version")
* Format: {provider}.{check_name}
* Must use lowercase, numbers, and underscores only
*/
public function getSlug(): string;
/**
* Get human-readable title (localized)
* Default implementation derives from language key:
* COM_HEALTHCHECKER_CHECK_{SLUG_UPPERCASE}_TITLE
*/
public function getTitle(): string;
/**
* Get category slug (e.g., "system", "security")
* Standard categories: system, database, security, users,
* extensions, performance, seo, content
* Or use custom category registered via CollectCategoriesEvent
*/
public function getCategory(): string;
/**
* Get provider slug (e.g., "core", "akeeba_backup")
* Identifies which plugin/component created this check
*/
public function getProvider(): string;
/**
* Execute the health check and return result
* Called by HealthCheckRunner during check execution
* Should be fast (< 1 second target)
*/
public function run(): HealthCheckResult;
}Contract requirements:
getSlug()MUST return unique identifier in format{provider}.{check_name}getTitle()MUST return translatable string (AbstractHealthCheck auto-implements this)getCategory()MUST return valid category slug (registered via event or built-in)getProvider()MUST match a provider registered viaCollectProvidersEventrun()MUST returnHealthCheckResult(never null, never throw unhandled exceptions if using AbstractHealthCheck)
AbstractHealthCheck
Base class that implements HealthCheckInterface with common functionality.
abstract class AbstractHealthCheck implements HealthCheckInterface
{
use DatabaseAwareTrait;
abstract protected function performCheck(): HealthCheckResult;
protected function critical(string $description): HealthCheckResult;
protected function warning(string $description): HealthCheckResult;
protected function good(string $description): HealthCheckResult;
public function getTitle(): string;
public function getProvider(): string;
public function run(): HealthCheckResult;
// Optional URL methods (return null by default)
public function getDocsUrl(?HealthStatus $healthStatus = null): ?string; // Override to add "Docs" button
public function getActionUrl(?HealthStatus $status = null): ?string; // Override to add "Explore" button
// Export visibility
public function getExportVisibility(): ExportVisibility; // Override to set developer default
public function setExportVisibility(ExportVisibility $exportVisibility): void; // Called by plugin to apply admin config
}Methods to implement:
performCheck()- Your check logic goes here
Helper methods:
critical($desc)- Return critical statuswarning($desc)- Return warning statusgood($desc)- Return good statusgetDatabase()- Access Joomla database (from DatabaseAwareTrait)
Auto-implemented:
getTitle()- Loads from language filegetProvider()- Returns 'core' by default (override if needed)run()- Wraps performCheck() with error handlinggetDocsUrl($healthStatus)- Returns null by default (override to add "Docs" button, receives check status to allow conditional display)getActionUrl($status)- Returns null by default (override to add "Explore" button, receives check status to allow conditional display)getExportVisibility()- ReturnsExportVisibility::Alwaysby default; override to change the developer default, or callsetExportVisibility()to apply an admin-configured valuesetExportVisibility($visibility)- Stores an admin-configured override; when set, it takes precedence overgetExportVisibility()
Error Handling
Automatic exception handling: The run() method in AbstractHealthCheck wraps your performCheck() implementation in a try/catch block:
final public function run(): HealthCheckResult
{
try {
return $this->performCheck();
} catch (\Throwable $throwable) {
return $this->warning(
Text::sprintf('COM_HEALTHCHECKER_CHECK_ERROR', $throwable->getMessage())
);
}
}What this means for you:
- Any uncaught exception in
performCheck()automatically returns a WARNING result - The exception message is included in the warning description
- Your check never crashes the entire health check run
- Database errors, network timeouts, file access issues are all handled gracefully
When to handle exceptions yourself:
protected function performCheck(): HealthCheckResult
{
try {
$result = $this->riskyOperation();
return $this->good('Operation successful');
} catch (SpecificException $e) {
// Custom handling for specific errors
return $this->critical('Known critical issue: ' . $e->getMessage());
}
// All other exceptions caught by AbstractHealthCheck
}Database Access
Null-safe database access:
// Option 1: Null-safe getter (returns null if not injected)
$db = $this->getDatabase();
if ($db === null) {
return $this->warning('Database not available');
}
// Option 2: Require database (throws exception if not injected)
$db = $this->requireDatabase(); // Throws RuntimeException if nullDatabase injection: The HealthCheckRunner automatically injects the database before calling run():
foreach ($checks as $check) {
if ($check instanceof AbstractHealthCheck) {
$check->setDatabase($this->database);
}
$this->results[] = $check->run();
}Check Execution Order
There is NO guaranteed execution order. Checks are:
- Collected from all plugins via
CollectChecksEvent - Stored in an array in the order plugins respond
- Executed sequentially in that arbitrary order
- Sorted by status (critical first) and category AFTER execution
Implications:
- Don't depend on another check running first
- Each check must be self-contained
- Checks run in series, not parallel (one at a time)
- Total run time = sum of all check times
Timeout Handling
Current behavior:
- No per-check timeout limit
- No global timeout limit
- Checks run until PHP's
max_execution_time(typically 30-300 seconds) - Slow checks block the entire health check run
Best practices:
- Target < 1 second per check
- Use timeouts for external HTTP requests
- Avoid expensive operations (large file scans, complex queries)
- Test with real data volumes
Example with timeout:
use Joomla\CMS\Http\HttpFactory;
protected function performCheck(): HealthCheckResult
{
$http = HttpFactory::getHttp([], ['timeout' => 5]); // 5 second timeout
try {
$response = $http->get('https://api.example.com/status');
return $this->good('API accessible');
} catch (\RuntimeException $e) {
return $this->warning('API timeout or unreachable');
}
}Data Classes
HealthCheckResult
Immutable result object returned by checks.
final readonly class HealthCheckResult
{
public function __construct(
public HealthStatus $healthStatus,
public string $title,
public string $description,
public string $slug,
public string $category,
public string $provider = 'core',
public ?string $docsUrl = null, // @since 3.0.36
public ?string $actionUrl = null, // @since 3.0.36
public ExportVisibility $exportVisibility = ExportVisibility::Always // @since 3.4.0
);
public function toArray(): array;
public static function fromArray(array $data): self;
}Properties:
healthStatus- One of: HealthStatus::Good, Warning, or Criticaltitle- Check titledescription- Result descriptionslug- Check slugcategory- Category slugprovider- Provider slugdocsUrl- URL for documentation link (displays ? icon) @since 3.0.36actionUrl- URL to navigate when row is clicked @since 3.0.36exportVisibility- Controls whether this result appears in exports @since 3.4.0
HealthStatus
Enum defining possible check statuses.
enum HealthStatus: string
{
case Critical = 'critical';
case Warning = 'warning';
case Good = 'good';
}ExportVisibility
Enum controlling whether a check result is included in exports. @since 3.4.0
enum ExportVisibility: string
{
case Always = 'always'; // Always included in exports (default)
case IssuesOnly = 'issues'; // Only included when Warning or Critical
case Never = 'never'; // Never included in exports
}The value is set on the check object before execution (via setExportVisibility()) and propagated to the HealthCheckResult. The export layer filters results based on this value before generating JSON or HTML output.
ProviderMetadata
Provider information for attribution.
final readonly class ProviderMetadata
{
public function __construct(
public string $slug,
public string $name,
public string $description = '',
public ?string $url = null,
public ?string $icon = null,
public ?string $logoUrl = null,
public ?string $version = null
);
}Required:
slug- Unique provider identifiername- Display name
Optional:
description- Brief descriptionurl- Website URLicon- FontAwesome icon classlogoUrl- Path to logo imageversion- Extension version
HealthCategory
Category definition for grouping checks.
final readonly class HealthCategory
{
public function __construct(
public string $slug,
public string $label,
public string $icon,
public int $sortOrder = 50
);
}Properties:
slug- Unique category identifierlabel- Language key for display nameicon- FontAwesome icon classsortOrder- Display order (lower first)
Events
CollectChecksEvent
Dispatched to collect health check instances.
final class CollectChecksEvent extends AbstractImmutableEvent
{
public function addResult(HealthCheckInterface $check): void;
public function getChecks(): array;
}Usage:
public function onCollectChecks(CollectChecksEvent $event): void
{
$check = new YourCheck();
$check->setDatabase($this->getDatabase());
$event->addResult($check);
}CollectCategoriesEvent
Dispatched to collect custom categories.
final class CollectCategoriesEvent extends AbstractImmutableEvent
{
public function addResult(HealthCategory $category): void;
public function getCategories(): array;
}Usage:
public function onCollectCategories(CollectCategoriesEvent $event): void
{
$event->addResult(new HealthCategory(
slug: 'custom',
label: 'YOUR_LANG_KEY',
icon: 'fa-icon',
sortOrder: 90
));
}CollectProvidersEvent
Dispatched to collect provider metadata.
final class CollectProvidersEvent extends AbstractImmutableEvent
{
public function addResult(ProviderMetadata $provider): void;
public function getProviders(): array;
}Usage:
public function onCollectProviders(CollectProvidersEvent $event): void
{
$event->addResult(new ProviderMetadata(
slug: 'yourplugin',
name: 'Your Plugin',
description: 'Health checks for Your Plugin'
));
}Plugin Structure
Minimal Plugin
<?php
namespace YourVendor\Plugin\HealthChecker\YourPlugin\Extension;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Event\SubscriberInterface;
final class YourPlugin extends CMSPlugin implements SubscriberInterface
{
use DatabaseAwareTrait;
public static function getSubscribedEvents(): array
{
return [
'onHealthCheckerCollectChecks' => 'onCollectChecks',
'onHealthCheckerCollectProviders' => 'onCollectProviders',
];
}
public function onCollectChecks(CollectChecksEvent $event): void
{
$check = new YourCheck();
$check->setDatabase($this->getDatabase());
$event->addResult($check);
}
public function onCollectProviders(CollectProvidersEvent $event): void
{
$event->addResult(new ProviderMetadata(
slug: 'yourplugin',
name: 'Your Plugin'
));
}
}Common Patterns
Database Query
protected function performCheck(): HealthCheckResult
{
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__your_table'));
$db->setQuery($query);
$count = (int) $db->loadResult();
return $count > 0
? $this->good("Found {$count} records")
: $this->warning('No records found');
}Configuration Check
use Joomla\CMS\Factory;
protected function performCheck(): HealthCheckResult
{
$config = Factory::getApplication()->get('setting_name');
return $config
? $this->good('Setting is enabled')
: $this->warning('Setting is disabled');
}File/Directory Check
use Joomla\CMS\Filesystem\File;
protected function performCheck(): HealthCheckResult
{
$path = JPATH_ROOT . '/required/file.txt';
if (!File::exists($path)) {
return $this->critical('Required file missing');
}
return $this->good('Required file exists');
}HTTP Request
use Joomla\CMS\Http\HttpFactory;
protected function performCheck(): HealthCheckResult
{
try {
$http = HttpFactory::getHttp();
$response = $http->get('https://api.example.com/status');
return $response->code === 200
? $this->good('API is accessible')
: $this->warning("API returned {$response->code}");
} catch (\Exception $e) {
return $this->critical("API unreachable: {$e->getMessage()}");
}
}Constants and Paths
Joomla Path Constants
JPATH_ROOT // Site root directory
JPATH_SITE // Site root (alias)
JPATH_ADMINISTRATOR // Admin directory
JPATH_LIBRARIES // Libraries directory
JPATH_PLUGINS // Plugins directory
JPATH_CACHE // Cache directory
JPATH_CONFIGURATION // Configuration directoryBest Practices
- Extend AbstractHealthCheck - Don't implement HealthCheckInterface directly
- Inject database - Always call
$check->setDatabase()when creating checks - Return early - Check critical conditions first
- Be specific - Detailed, actionable descriptions
- Handle errors - Let AbstractHealthCheck catch exceptions or handle explicitly
- Stay fast - Target under 1 second per check
- Read-only - Never modify data in checks
Next Steps
- Creating Checks - Build your first check
- Examples - See complete working examples
- Best Practices - Code conventions and guidelines