-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
The project enforces strict rules:
- No singleton
- No static methods/properties
- No final classes/methods
- Dependency Injection via constructor
- Interface-first design
ops-health-dashboard.php
- defines plugin constants
- verifies Composer autoloader
- registers activation/deactivation hooks
- on
plugins_loadedcallsOpsHealthDashboard\\bootstrap()
config/bootstrap.php
- creates
Container - registers
$wpdbviainstance('wpdb', $wpdb) - registers shared bindings (
share) for services, checks, admin - returns
new Plugin($container)
src/Core/Container.php
-
bind(abstract, closure)for non-shared instances -
share(abstract, closure)for container-managed shared instances -
instance(abstract, instance)for already-instantiated objects -
make(abstract)with circular dependency detection
The container uses a $resolving array to track abstracts being resolved. If make() is called for an abstract already present in $resolving, an exception is thrown:
if ( isset( $this->resolving[ $abstract ] ) ) {
throw new \Exception( "Circular dependency detected for [{$abstract}]" );
}
$this->resolving[ $abstract ] = true;
try {
// resolve...
} finally {
unset( $this->resolving[ $abstract ] );
}The finally block ensures the array is cleaned up even if an exception occurs, allowing the container to continue functioning after an error.
src/Services/Storage.php
The has() method uses a sentinel object to distinguish between "the key exists with value false" and "the key does not exist":
public function has( string $key ): bool {
$sentinel = new \stdClass();
$value = get_option( $prefixed_key, $sentinel );
return $sentinel !== $value;
}get_option() returns the default when the key is missing. Using a stdClass as the default, the !== comparison is safe because each new stdClass() is a unique object.
Note: update_option() is called with autoload=false to prevent large data (such as check results) from being loaded into memory on every WordPress request.
src/Core/Plugin.php
-
init()is idempotent with an internal flag - registers hooks for:
- Admin menu
- Dashboard widget
- HealthScreen styles enqueue
- Scheduler cron
-
Core: lifecycle, container, orchestration -
Interfaces: contracts (CheckInterface,StorageInterface,HttpClientInterface, etc.) -
Services: application logic (scheduler, runner, storage, redaction, http client, alert manager) -
Checks: concrete health checks -
Channels: concrete alert channels -
Admin: menu and wp-admin pages
Tier 0 (foundation):
StorageInterface, RedactionInterface
Tier 1 (infrastructure):
HttpClientInterface ← used by all outbound channels
CheckInterface[] ← 5 concrete checks
Tier 2 (orchestration):
CheckRunnerInterface ← StorageInterface, RedactionInterface, CheckInterface[]
AlertChannelInterface[] ← StorageInterface, HttpClientInterface
AlertManagerInterface ← StorageInterface, RedactionInterface, AlertChannelInterface[]
Tier 3 (application):
Scheduler ← CheckRunnerInterface, AlertManagerInterface
Plugin ← Menu, DashboardWidget, HealthScreen, Scheduler
Direct dependencies:
-
Plugin→Menu,DashboardWidget,HealthScreen,Scheduler -
Scheduler→CheckRunnerInterface,AlertManagerInterface -
CheckRunner→StorageInterface,RedactionInterface,CheckInterface[] -
AlertManager→StorageInterface,RedactionInterface,AlertChannelInterface[] -
Webhook/Slack/Telegram/WhatsApp→StorageInterface,HttpClientInterface -
Email→StorageInterface(useswp_mail(), does not needHttpClientInterface)