Remove WireGuard integration from production deployment to simplify infrastructure: - Remove docker-compose-direct-access.yml (VPN-bound services) - Remove VPN-only middlewares from Grafana, Prometheus, Portainer - Remove WireGuard middleware definitions from Traefik - Remove WireGuard IPs (10.8.0.0/24) from Traefik forwarded headers All monitoring services now publicly accessible via subdomains: - grafana.michaelschiemer.de (with Grafana native auth) - prometheus.michaelschiemer.de (with Basic Auth) - portainer.michaelschiemer.de (with Portainer native auth) All services use Let's Encrypt SSL certificates via Traefik.
136 lines
3.1 KiB
PHP
136 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\ExceptionHandling\Scope;
|
|
|
|
use App\Framework\DI\Initializer;
|
|
use Fiber;
|
|
|
|
/**
|
|
* Error Scope Stack Manager
|
|
*
|
|
* Manages fiber-aware scope stack for error context enrichment.
|
|
* Each fiber has its own isolated scope stack.
|
|
*
|
|
* PHP 8.5+ with fiber isolation and automatic cleanup.
|
|
*/
|
|
final class ErrorScope
|
|
{
|
|
/** @var array<int, array<ErrorScopeContext>> Fiber-specific scope stacks */
|
|
private array $stack = [];
|
|
|
|
#[Initializer]
|
|
public static function initialize(): self
|
|
{
|
|
return new self();
|
|
}
|
|
|
|
/**
|
|
* Enter a new error scope
|
|
*
|
|
* @param ErrorScopeContext $context Scope context to enter
|
|
* @return int Token for leaving this scope (stack depth)
|
|
*/
|
|
public function enter(ErrorScopeContext $context): int
|
|
{
|
|
$id = $this->fiberId();
|
|
$this->stack[$id] ??= [];
|
|
$this->stack[$id][] = $context;
|
|
|
|
return count($this->stack[$id]);
|
|
}
|
|
|
|
/**
|
|
* Exit error scope(s)
|
|
*
|
|
* @param int $token Token from enter() - exits all scopes until this depth
|
|
*/
|
|
public function exit(int $token = 0): void
|
|
{
|
|
$id = $this->fiberId();
|
|
|
|
if (!isset($this->stack[$id])) {
|
|
return;
|
|
}
|
|
|
|
if ($token === 0) {
|
|
// Exit only the most recent scope
|
|
array_pop($this->stack[$id]);
|
|
} else {
|
|
// Exit all scopes until token depth
|
|
while (!empty($this->stack[$id]) && count($this->stack[$id]) >= $token) {
|
|
array_pop($this->stack[$id]);
|
|
}
|
|
}
|
|
|
|
// Cleanup empty stack
|
|
if (empty($this->stack[$id])) {
|
|
unset($this->stack[$id]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get current error scope context
|
|
*
|
|
* @return ErrorScopeContext|null Current scope or null if no scope active
|
|
*/
|
|
public function current(): ?ErrorScopeContext
|
|
{
|
|
$id = $this->fiberId();
|
|
$stack = $this->stack[$id] ?? [];
|
|
|
|
$current = end($stack);
|
|
return $current !== false ? $current : null;
|
|
}
|
|
|
|
/**
|
|
* Check if any scope is active
|
|
*/
|
|
public function hasScope(): bool
|
|
{
|
|
$id = $this->fiberId();
|
|
return !empty($this->stack[$id]);
|
|
}
|
|
|
|
/**
|
|
* Get scope depth (number of nested scopes)
|
|
*/
|
|
public function depth(): int
|
|
{
|
|
$id = $this->fiberId();
|
|
return count($this->stack[$id] ?? []);
|
|
}
|
|
|
|
/**
|
|
* Get fiber ID for isolation
|
|
*
|
|
* Returns 0 for main fiber, unique ID for each Fiber
|
|
*/
|
|
private function fiberId(): int
|
|
{
|
|
$fiber = Fiber::getCurrent();
|
|
return $fiber ? spl_object_id($fiber) : 0;
|
|
}
|
|
|
|
/**
|
|
* Clear all scopes (for testing/cleanup)
|
|
*/
|
|
public function clear(): void
|
|
{
|
|
$this->stack = [];
|
|
}
|
|
|
|
/**
|
|
* Get statistics for monitoring
|
|
*/
|
|
public function getStats(): array
|
|
{
|
|
return [
|
|
'active_fibers' => count($this->stack),
|
|
'total_scopes' => array_sum(array_map('count', $this->stack)),
|
|
'max_depth' => !empty($this->stack) ? max(array_map('count', $this->stack)) : 0,
|
|
];
|
|
}
|
|
}
|