Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
@@ -4,84 +4,286 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Http;
|
||||
|
||||
use App\Framework\Core\InterfaceImplementationLocator;
|
||||
use App\Framework\Cache\Cache;
|
||||
use App\Framework\Core\ValueObjects\ClassName;
|
||||
use App\Framework\DI\Container;
|
||||
use App\Framework\Discovery\Results\DiscoveryResults;
|
||||
use App\Framework\Http\HttpMiddleware;
|
||||
use App\Framework\Http\Middlewares\ControllerRequestMiddleware;
|
||||
use App\Framework\Http\Middlewares\RoutingMiddleware;
|
||||
use App\Framework\Validation\ValidationErrorMiddleware;
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Http\Middlewares\DDoSProtectionMiddleware;
|
||||
use App\Framework\Http\Middlewares\RateLimitMiddleware;
|
||||
use App\Framework\Http\Middlewares\WafMiddleware;
|
||||
use App\Framework\Logging\Logger;
|
||||
use App\Framework\Reflection\ReflectionProvider;
|
||||
|
||||
/**
|
||||
* Verwaltet die HTTP-Middleware-Pipeline
|
||||
*/
|
||||
final readonly class MiddlewareManager
|
||||
final readonly class MiddlewareManager implements MiddlewareManagerInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string> Array mit Middleware-Klassen
|
||||
*/
|
||||
#private array $middlewares = [];
|
||||
|
||||
public HttpMiddlewareChain $chain;
|
||||
|
||||
public function __construct(
|
||||
private Container $container,
|
||||
private DiscoveryResults $discoveryResults,
|
||||
)
|
||||
{
|
||||
// Standard-Middlewares registrieren
|
||||
/*$this->middlewares = [
|
||||
ControllerRequestMiddleware::class,
|
||||
ValidationErrorMiddleware::class,
|
||||
RoutingMiddleware::class
|
||||
];*/
|
||||
|
||||
private DiscoveryRegistry $discoveryRegistry,
|
||||
private ReflectionProvider $reflectionProvider,
|
||||
private Cache $cache,
|
||||
) {
|
||||
$middlewares = $this->buildMiddlewareStack();
|
||||
|
||||
foreach($middlewares as $middleware) {
|
||||
#echo $middleware . " " . $this->getMiddlewarePriority($middleware) . "<br>". PHP_EOL;
|
||||
}
|
||||
|
||||
error_log("MiddlewareManager: Middleware stack: " . implode(', ', array_map(fn ($m) => basename($m), $middlewares)));
|
||||
|
||||
$this->chain = new HttpMiddlewareChain(
|
||||
$middlewares,
|
||||
fn() => new HttpResponse(Status::NOT_FOUND),
|
||||
$this->container
|
||||
);
|
||||
}
|
||||
|
||||
private function buildMiddlewareStack(): array
|
||||
{
|
||||
$middlewares = $this->discoveryResults->get(HttpMiddleware::class);
|
||||
$middlewares = array_column($middlewares, 'class');
|
||||
return $this->sortMiddlewaresByPriority($middlewares);
|
||||
// Explizite Reihenfolge definieren - wichtigste zuerst
|
||||
$explicitOrder = [
|
||||
// 0. Exception Handling - MUSS absolut zuerst kommen, um alle Exceptions zu fangen
|
||||
\App\Framework\Http\Middlewares\ExceptionHandlingMiddleware::class,
|
||||
|
||||
// 1. System und Error Handling
|
||||
\App\Framework\Http\Middlewares\RequestIdMiddleware::class,
|
||||
|
||||
// 2. Security und DDoS Protection
|
||||
DDoSProtectionMiddleware::class,
|
||||
WafMiddleware::class,
|
||||
|
||||
// 3. Session - MUSS vor Auth und CSRF kommen!
|
||||
\App\Framework\Http\Session\SessionMiddleware::class,
|
||||
|
||||
// 4. Security und Rate Limiting
|
||||
RateLimitMiddleware::class,
|
||||
#\App\Application\Security\Middleware\SecurityEventMiddleware::class,
|
||||
|
||||
// 5. Headers und CORS
|
||||
\App\Framework\Http\Middlewares\SecurityHeaderMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\RemovePoweredByMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\CORSMiddleware::class,
|
||||
|
||||
// 6. Authentication und CSRF (brauchen Session)
|
||||
\App\Framework\Http\Middlewares\AuthMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\CsrfMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\HoneypotMiddleware::class,
|
||||
|
||||
// 7. Routing und Request Processing
|
||||
\App\Framework\Http\Middlewares\RoutingMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\ControllerRequestMiddleware::class,
|
||||
|
||||
// 8. Content und Static Files
|
||||
#\App\Framework\Http\Middlewares\ServeStaticFilesMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\ResponseGeneratorMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\FormDataResponseMiddleware::class, // Temporarily disabled
|
||||
|
||||
// 9. Monitoring und Analytics
|
||||
\App\Framework\Analytics\Middleware\AnalyticsMiddleware::class,
|
||||
\App\Framework\Performance\Middleware\RequestPerformanceMiddleware::class,
|
||||
\App\Framework\Performance\Middleware\RoutingPerformanceMiddleware::class,
|
||||
\App\Framework\Performance\Middleware\ControllerPerformanceMiddleware::class,
|
||||
\App\Framework\Tracing\TracingMiddleware::class,
|
||||
|
||||
// 10. Logging (am Ende)
|
||||
\App\Framework\Http\Middlewares\RequestLoggingMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\LoggingMiddleware::class,
|
||||
\App\Framework\Performance\Middleware\PerformanceDebugMiddleware::class,
|
||||
\App\Framework\Security\RequestSigning\RequestSigningMiddleware::class,
|
||||
|
||||
// 11. FALLBACK - Absolut letztes Middleware (nur wenn keine Response vorhanden)
|
||||
\App\Framework\Http\Middlewares\DefaultResponseMiddleware::class,
|
||||
];
|
||||
|
||||
// Use the refactored robust dependency resolution
|
||||
try {
|
||||
$resolved = $this->resolveDependencies($explicitOrder);
|
||||
#error_log("MiddlewareManager: Using dependency resolution - Count: " . count($resolved));
|
||||
|
||||
// WICHTIG: Array umkehren, da die Liste in gewünschter Ausführungsreihenfolge definiert ist
|
||||
// aber die Middleware-Chain sie in der gegebenen Reihenfolge abarbeitet
|
||||
return array_reverse($resolved);
|
||||
} catch (\Throwable $e) {
|
||||
#error_log("MiddlewareManager: Dependency resolution failed: " . $e->getMessage());
|
||||
#error_log("MiddlewareManager: Falling back to explicit order - Count: " . count($explicitOrder));
|
||||
#foreach ($explicitOrder as $i => $middleware) {
|
||||
# error_log("MiddlewareManager: Explicit #{$i}: " . basename($middleware));
|
||||
#}
|
||||
|
||||
// WICHTIG: Array umkehren, da die Liste in gewünschter Ausführungsreihenfolge definiert ist
|
||||
return array_reverse($explicitOrder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine Middleware-Chain mit den registrierten Middlewares
|
||||
* Resolve middleware dependencies using constructor analysis
|
||||
* @param array<string> $middlewares
|
||||
* @return array<string>
|
||||
*/
|
||||
/*public function createMiddlewareChain(callable $fallbackHandler): HttpMiddlewareChain
|
||||
private function resolveDependencies(array $middlewares): array
|
||||
{
|
||||
return new HttpMiddlewareChain(
|
||||
$this->middlewares,
|
||||
$fallbackHandler,
|
||||
$this->container
|
||||
$logger = $this->container->get(Logger::class);
|
||||
$resolver = new MiddlewareDependencyResolver(
|
||||
$this->reflectionProvider,
|
||||
$this->container,
|
||||
$logger
|
||||
);
|
||||
}*/
|
||||
|
||||
$resolved = $resolver->resolve($middlewares);
|
||||
|
||||
// Log dependency resolution info
|
||||
#error_log("MiddlewareManager: Dependency resolution completed");
|
||||
#error_log("MiddlewareManager: Resolved " . $resolved->count() . " middlewares");
|
||||
#error_log("MiddlewareManager: Execution order: " . implode(' → ', array_map(fn ($m) => basename($m), $resolved->getMiddlewares())));
|
||||
|
||||
// Log dependency chains for debugging
|
||||
$info = $resolved->getResolutionInfo();
|
||||
#if (! empty($info['dependency_chains'])) {
|
||||
# error_log("MiddlewareManager: Dependency chains: " . json_encode($info['dependency_chains']));
|
||||
#}
|
||||
|
||||
#if (! empty($info['circular_dependencies'])) {
|
||||
# error_log("MiddlewareManager: WARNING - Circular dependencies detected: " . json_encode($info['circular_dependencies']));
|
||||
#}
|
||||
|
||||
return $resolved->getMiddlewares();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get middleware dependency information for debugging
|
||||
*/
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getDependencyInfo(): array
|
||||
{
|
||||
$explicitOrder = $this->getExplicitOrder();
|
||||
$classNameObjects = array_map(fn ($class) => ClassName::create($class), $explicitOrder);
|
||||
|
||||
$logger = $this->container->get(Logger::class);
|
||||
$resolver = new MiddlewareDependencyResolver(
|
||||
$this->reflectionProvider,
|
||||
$this->container,
|
||||
$logger
|
||||
);
|
||||
|
||||
return $resolver->getDependencyInfo($classNameObjects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the explicit order array (for testing and debugging)
|
||||
* @return array<string>
|
||||
*/
|
||||
private function getExplicitOrder(): array
|
||||
{
|
||||
// Return the same explicit order array
|
||||
return [
|
||||
// 1. System und Error Handling
|
||||
\App\Framework\Http\Middlewares\RequestIdMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\ExceptionHandlingMiddleware::class,
|
||||
|
||||
// 4. Session - MUSS vor Auth und CSRF kommen!
|
||||
\App\Framework\Http\Session\SessionMiddleware::class,
|
||||
|
||||
// 2. Security und Rate Limiting
|
||||
RateLimitMiddleware::class,
|
||||
#\App\Application\Security\Middleware\SecurityEventMiddleware::class,
|
||||
|
||||
// 3. Headers und CORS
|
||||
\App\Framework\Http\Middlewares\SecurityHeaderMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\RemovePoweredByMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\CORSMiddleware::class,
|
||||
|
||||
// 5. Authentication und CSRF (brauchen Session)
|
||||
\App\Framework\Http\Middlewares\AuthMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\CsrfMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\HoneypotMiddleware::class,
|
||||
|
||||
// 6. Routing und Request Processing
|
||||
\App\Framework\Http\Middlewares\RoutingMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\ControllerRequestMiddleware::class,
|
||||
|
||||
// 7. Content und Static Files
|
||||
#\App\Framework\Http\Middlewares\ServeStaticFilesMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\ResponseGeneratorMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\FormDataResponseMiddleware::class, // Temporarily disabled
|
||||
|
||||
// 8. Monitoring und Analytics
|
||||
\App\Framework\Analytics\Middleware\AnalyticsMiddleware::class,
|
||||
\App\Framework\Performance\Middleware\RequestPerformanceMiddleware::class,
|
||||
\App\Framework\Performance\Middleware\RoutingPerformanceMiddleware::class,
|
||||
\App\Framework\Performance\Middleware\ControllerPerformanceMiddleware::class,
|
||||
\App\Framework\Tracing\TracingMiddleware::class,
|
||||
|
||||
// 9. Logging (am Ende)
|
||||
\App\Framework\Http\Middlewares\RequestLoggingMiddleware::class,
|
||||
\App\Framework\Http\Middlewares\LoggingMiddleware::class,
|
||||
\App\Framework\Performance\Middleware\PerformanceDebugMiddleware::class,
|
||||
\App\Framework\Security\RequestSigning\RequestSigningMiddleware::class,
|
||||
|
||||
// 10. FALLBACK - Absolut letztes Middleware (nur wenn keine Response vorhanden)
|
||||
\App\Framework\Http\Middlewares\DefaultResponseMiddleware::class,
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
public function getPriorityForClass(object|string $middlewareClass): int
|
||||
{
|
||||
$className = is_string($middlewareClass) ? $middlewareClass : $middlewareClass::class;
|
||||
$className = ltrim($className, '\\');
|
||||
|
||||
$cacheKey = "middleware_priority:{$className}";
|
||||
|
||||
$cacheItem = $this->cache->remember($cacheKey, function () use ($className) {
|
||||
return $this->resolvePriorityFromReflection($className);
|
||||
}, 3600);
|
||||
|
||||
return is_int($cacheItem->value) ? $cacheItem->value : MiddlewarePriority::BUSINESS_LOGIC->value;
|
||||
}
|
||||
|
||||
private function resolvePriorityFromReflection(string $className): int
|
||||
{
|
||||
try {
|
||||
$classNameObj = ClassName::create($className);
|
||||
$attrs = $this->reflectionProvider->getAttributes($classNameObj, MiddlewarePriorityAttribute::class);
|
||||
|
||||
if ($attrs->count() > 0) {
|
||||
/** @var MiddlewarePriorityAttribute|null $attr */
|
||||
$attr = $attrs->getFirstByType(MiddlewarePriorityAttribute::class)?->newInstance();
|
||||
if ($attr instanceof MiddlewarePriorityAttribute) {
|
||||
$basePriority = $attr->priority->value;
|
||||
$offset = $attr->offset ?? 0;
|
||||
|
||||
return $basePriority + $offset;
|
||||
}
|
||||
}
|
||||
} catch (\ReflectionException $e) {
|
||||
// Class doesn't exist or can't be reflected, use default
|
||||
}
|
||||
|
||||
// Default priority
|
||||
return MiddlewarePriority::BUSINESS_LOGIC->value;
|
||||
}
|
||||
|
||||
// Legacy static method for backward compatibility
|
||||
public static function getPriority(object|string $middlewareClass): int
|
||||
{
|
||||
$reflection = new \ReflectionClass($middlewareClass);
|
||||
$attrs = $reflection->getAttributes(MiddlewarePriorityAttribute::class);
|
||||
if ($attrs) {
|
||||
/** @var MiddlewarePriorityAttribute $attr */
|
||||
$attr = $attrs[0]->newInstance();
|
||||
if ($attr->offset !== 0) {
|
||||
return $attr->priority->value + $attr->offset;
|
||||
// This is a fallback for any code that still uses the static method
|
||||
$className = is_string($middlewareClass) ? $middlewareClass : $middlewareClass::class;
|
||||
$className = ltrim($className, '\\');
|
||||
|
||||
try {
|
||||
$reflection = new \ReflectionClass($className);
|
||||
$attrs = $reflection->getAttributes(MiddlewarePriorityAttribute::class);
|
||||
|
||||
if ($attrs) {
|
||||
/** @var MiddlewarePriorityAttribute $attr */
|
||||
$attr = $attrs[0]->newInstance();
|
||||
$basePriority = $attr->priority->value;
|
||||
$offset = $attr->offset ?? 0;
|
||||
|
||||
return $basePriority + $offset;
|
||||
}
|
||||
return $attr->priority->value;
|
||||
} catch (\ReflectionException $e) {
|
||||
// Class doesn't exist or can't be reflected, use default
|
||||
}
|
||||
// Standard-Priorität (oder Fehlermeldung)
|
||||
|
||||
return MiddlewarePriority::BUSINESS_LOGIC->value;
|
||||
}
|
||||
|
||||
@@ -101,24 +303,52 @@ final readonly class MiddlewareManager
|
||||
// Hilfsmethode zum Abrufen der Middleware-Priorität
|
||||
private function getMiddlewarePriority(object|string $middlewareClass): int
|
||||
{
|
||||
$priority = $this->discoveryResults->get(MiddlewarePriorityAttribute::class);
|
||||
$middlewareClass = ltrim($middlewareClass, '\\');
|
||||
$className = is_string($middlewareClass) ? $middlewareClass : $middlewareClass::class;
|
||||
$className = ltrim($className, '\\');
|
||||
|
||||
foreach($priority as $item) {
|
||||
#dd($middlewareClass);
|
||||
if ($item['class'] === $middlewareClass) {
|
||||
#debug($item['attribute_data']['priority']);
|
||||
#dd($item['attribute_data']['priority']->value);
|
||||
$priority = $item['attribute_data']['priority'];
|
||||
if(!is_int($priority)) {
|
||||
$priority = $priority->value;
|
||||
$cacheKey = "middleware_priority_discovery:{$className}";
|
||||
|
||||
$cacheItem = $this->cache->remember($cacheKey, function () use ($className) {
|
||||
// Use discovery registry to find priority
|
||||
$priorities = $this->discoveryRegistry->attributes()->get(MiddlewarePriorityAttribute::class);
|
||||
|
||||
foreach ($priorities as $discoveredAttribute) {
|
||||
if ($discoveredAttribute->className->getFullyQualified() === $className) {
|
||||
$additionalData = $discoveredAttribute->additionalData;
|
||||
$priority = $additionalData['priority'] ?? null;
|
||||
if (! is_int($priority)) {
|
||||
$priority = $priority->value;
|
||||
}
|
||||
|
||||
return $priority + ($additionalData['offset'] ?? 0);
|
||||
}
|
||||
|
||||
return $priority + ($item['attribute_data']['offset'] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware has no Priority: Set to default
|
||||
return MiddlewarePriority::BUSINESS_LOGIC->value;
|
||||
// Default priority
|
||||
return MiddlewarePriority::BUSINESS_LOGIC->value;
|
||||
}, 3600);
|
||||
|
||||
return is_int($cacheItem->value) ? $cacheItem->value : MiddlewarePriority::BUSINESS_LOGIC->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the priority cache (useful for testing)
|
||||
*/
|
||||
public function clearPriorityCache(): void
|
||||
{
|
||||
// Clear cache entries that start with our middleware priority prefix
|
||||
$this->cache->forget('middleware_priority:*');
|
||||
$this->cache->forget('middleware_priority_discovery:*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache statistics for debugging
|
||||
*/
|
||||
public function getCacheStats(): array
|
||||
{
|
||||
return [
|
||||
'cache_implementation' => get_class($this->cache),
|
||||
'reflection_provider' => get_class($this->reflectionProvider),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user