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:
@@ -1,104 +1,209 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Core;
|
||||
|
||||
use App\Framework\Cache\Cache;
|
||||
use App\Framework\Cache\CacheInitializer;
|
||||
use App\Framework\Config\Configuration;
|
||||
use App\Framework\DateTime\Clock;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\Container;
|
||||
use App\Framework\DI\Initializer;
|
||||
use App\Framework\DI\ContainerCompiler;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\DI\DependencyResolver;
|
||||
use App\Framework\Discovery\DiscoveryServiceBootstrapper;
|
||||
use App\Framework\Discovery\Results\DiscoveryResults;
|
||||
use App\Framework\Http\ResponseEmitter;
|
||||
use App\Framework\Performance\PerformanceMeter;
|
||||
use App\Framework\Attributes\Route;
|
||||
use App\Framework\Logging\DefaultLogger;
|
||||
use App\Framework\Logging\Logger;
|
||||
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
|
||||
final readonly class ContainerBootstrapper
|
||||
{
|
||||
public function __construct(
|
||||
private Container $container)
|
||||
{}
|
||||
private Container $container,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap container with intelligent compilation strategy
|
||||
*/
|
||||
public function bootstrap(
|
||||
string $basePath,
|
||||
PerformanceMeter $meter,
|
||||
array $config = []): void
|
||||
{
|
||||
// Kern-Dienste im Container registrieren:
|
||||
$this->container->instance(PerformanceMeter::class, $meter);
|
||||
$this->container->instance(Cache::class, new CacheInitializer()());
|
||||
$this->container->instance(Configuration::class, new Configuration($config));
|
||||
$this->container->instance(PathProvider::class, new PathProvider($basePath));
|
||||
$this->container->instance(ResponseEmitter::class, new ResponseEmitter());
|
||||
PerformanceCollectorInterface $collector,
|
||||
): Container {
|
||||
// Temporarily disable compiled container to fix bootstrap issues
|
||||
// TODO: Re-enable once container interface compatibility is fixed
|
||||
// $optimizedContainer = $this->tryLoadCompiledContainer($basePath, $collector);
|
||||
// if ($optimizedContainer !== null) {
|
||||
// return $optimizedContainer;
|
||||
// }
|
||||
|
||||
$this->autowire($this->container->get(PathProvider::class), $this->container->get(Configuration::class));
|
||||
// Fallback to fresh container bootstrap
|
||||
return $this->bootstrapFreshContainer($basePath, $collector);
|
||||
}
|
||||
|
||||
private function autowire(PathProvider $pathProvider, Configuration $configuration):void
|
||||
/**
|
||||
* Try to load compiled container if valid
|
||||
*/
|
||||
private function tryLoadCompiledContainer(
|
||||
string $basePath,
|
||||
PerformanceCollectorInterface $collector
|
||||
): ?Container {
|
||||
try {
|
||||
$cacheDir = sys_get_temp_dir() . '/framework-cache';
|
||||
$compiledPath = ContainerCompiler::getCompiledContainerPath($cacheDir);
|
||||
|
||||
// Quick existence check first
|
||||
if (! file_exists($compiledPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create temporary fresh container to validate against
|
||||
$tempContainer = $this->createFreshContainer($basePath, $collector);
|
||||
|
||||
// Create compiler for validation
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
$dependencyResolver = new DependencyResolver($reflectionProvider, $tempContainer);
|
||||
$compiler = new ContainerCompiler($reflectionProvider, $dependencyResolver);
|
||||
|
||||
// Validate compiled container
|
||||
if (! $compiler->isCompiledContainerValid($tempContainer, $compiledPath)) {
|
||||
return null; // Invalid, will trigger fresh bootstrap
|
||||
}
|
||||
|
||||
// Load and return compiled container
|
||||
$compiledContainer = ContainerCompiler::load($compiledPath);
|
||||
|
||||
// Add runtime instances that can't be compiled
|
||||
$this->addRuntimeInstances($compiledContainer, $basePath, $collector);
|
||||
|
||||
return $compiledContainer;
|
||||
|
||||
} catch (\ParseError $e) {
|
||||
// Parse error in compiled container - delete cache and fallback
|
||||
$this->clearCompiledCache($compiledPath);
|
||||
error_log("Compiled container has syntax error, deleted cache: " . $e->getMessage());
|
||||
|
||||
return null;
|
||||
} catch (\Exception $e) {
|
||||
// Any other error means we fallback to fresh container
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap fresh container and compile for next request
|
||||
*/
|
||||
private function bootstrapFreshContainer(
|
||||
string $basePath,
|
||||
PerformanceCollectorInterface $collector
|
||||
): Container {
|
||||
$container = $this->createFreshContainer($basePath, $collector);
|
||||
|
||||
// Compile for next request (async in production)
|
||||
$this->compileContainerAsync($container);
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create fresh container with all bindings
|
||||
*/
|
||||
private function createFreshContainer(
|
||||
string $basePath,
|
||||
PerformanceCollectorInterface $collector
|
||||
): DefaultContainer {
|
||||
// Use the existing container or create new DefaultContainer
|
||||
$container = $this->container instanceof DefaultContainer
|
||||
? $this->container
|
||||
: new DefaultContainer();
|
||||
|
||||
$this->addRuntimeInstances($container, $basePath, $collector);
|
||||
$this->autowire($container);
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add instances that must be created at runtime
|
||||
*/
|
||||
private function addRuntimeInstances(
|
||||
Container $container,
|
||||
string $basePath,
|
||||
PerformanceCollectorInterface $collector
|
||||
): void {
|
||||
// Core services that need runtime data
|
||||
$container->instance(Logger::class, new DefaultLogger());
|
||||
$container->instance(PerformanceCollectorInterface::class, $collector);
|
||||
$container->instance(Cache::class, new CacheInitializer($collector, $container)());
|
||||
$container->instance(PathProvider::class, new PathProvider($basePath));
|
||||
$container->instance(ResponseEmitter::class, new ResponseEmitter());
|
||||
$container->instance(Clock::class, new SystemClock());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile container asynchronously for next request
|
||||
*/
|
||||
private function compileContainerAsync(DefaultContainer $container): void
|
||||
{
|
||||
// Im ContainerBootstrapper
|
||||
$bootstrapper = new DiscoveryServiceBootstrapper($this->container);
|
||||
try {
|
||||
$cacheDir = sys_get_temp_dir() . '/framework-cache';
|
||||
$compiledPath = ContainerCompiler::getCompiledContainerPath($cacheDir);
|
||||
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
$dependencyResolver = new DependencyResolver($reflectionProvider, $container);
|
||||
$compiler = new ContainerCompiler($reflectionProvider, $dependencyResolver);
|
||||
|
||||
// Compile (async in production, sync in development)
|
||||
#$isProduction = $this->config->get('app.environment') === 'production';
|
||||
$isProduction = false;
|
||||
if ($isProduction) {
|
||||
$compiler->compileAsync($container, $compiledPath);
|
||||
} else {
|
||||
$compiler->compile($container, $compiledPath);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Compilation errors should not break the application
|
||||
// In production, log this error
|
||||
}
|
||||
}
|
||||
|
||||
private function autowire(Container $container): void
|
||||
{
|
||||
// Discovery service bootstrapping
|
||||
$clock = $container->get(Clock::class);
|
||||
$bootstrapper = new DiscoveryServiceBootstrapper($container, $clock);
|
||||
$results = $bootstrapper->bootstrap();
|
||||
}
|
||||
|
||||
// Ergebnisse sind automatisch im Container verfügbar
|
||||
/** @var DiscoveryResults $results */
|
||||
$results = $this->container->get(DiscoveryResults::class);
|
||||
/**
|
||||
* Clear compiled container cache files
|
||||
*/
|
||||
private function clearCompiledCache(string $compiledPath): void
|
||||
{
|
||||
try {
|
||||
// Delete the compiled container file
|
||||
if (file_exists($compiledPath)) {
|
||||
@unlink($compiledPath);
|
||||
}
|
||||
|
||||
#$routeResults = $results->get(Route::class);
|
||||
|
||||
|
||||
// Fallback: Versuche auch mit führendem Backslash
|
||||
/*if (empty($routeResults)) {
|
||||
$routeResults = $results->get('\\' . $routeClass);
|
||||
debug('Tried with leading backslash');
|
||||
}*/
|
||||
|
||||
// Fallback: Suche in allen verfügbaren Keys
|
||||
/*if (empty($routeResults)) {
|
||||
foreach (array_keys($results->getAllAttributeResults()) as $key) {
|
||||
if (str_contains($key, 'Route')) {
|
||||
debug("Found Route-related key: $key");
|
||||
$routeResults = $results->get($key);
|
||||
break;
|
||||
// Also clear related cache directory if it exists
|
||||
$cacheDir = dirname($compiledPath);
|
||||
if (is_dir($cacheDir)) {
|
||||
// Clear all cache files in the directory
|
||||
$files = glob($cacheDir . '/*');
|
||||
foreach ($files as $file) {
|
||||
if (is_file($file)) {
|
||||
@unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
#$discovery = new AttributeDiscoveryService($this->container, $pathProvider, $configuration);
|
||||
|
||||
#$dresults = ProcessedResults::fromArray($discovery->discoverAndProcess())->get(Route::class);
|
||||
|
||||
#debug(count($routeResults));
|
||||
#dd(count($dresults));
|
||||
|
||||
|
||||
|
||||
|
||||
#$cache = $this->container->get(Cache::class);
|
||||
|
||||
#$cache->forget('discovery_service');
|
||||
|
||||
#$processedResults = $cache->remember('discovery_service', function () use ($discovery) {
|
||||
#
|
||||
# // Attribute entdecken und verarbeiten
|
||||
# return $discovery->discoverAndProcess();
|
||||
#
|
||||
# })->value;
|
||||
|
||||
#$results = ProcessedResults::fromArray($processedResults);
|
||||
|
||||
#$this->container->instance(ProcessedResults::class, $results);
|
||||
|
||||
#$this->executeInitializers($results);
|
||||
|
||||
}
|
||||
|
||||
private function executeInitializers(DiscoveryResults $initializerClasses): void
|
||||
{
|
||||
foreach ($initializerClasses->get(Initializer::class) as $initializerClass) {
|
||||
|
||||
$instance = $this->container->invoker->invoke($initializerClass['class'], $initializerClass['method']);
|
||||
|
||||
$this->container->instance($initializerClass['return'], $instance);
|
||||
} catch (\Throwable $e) {
|
||||
// Ignore cache cleanup errors, just log them
|
||||
error_log("Error clearing compiled cache: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user