Files
michaelschiemer/src/Framework/Core/ContainerBootstrapper.php
Michael Schiemer 5050c7d73a docs: consolidate documentation into organized structure
- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
2025-10-05 11:05:04 +02:00

228 lines
8.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Core;
use App\Framework\Cache\Cache;
use App\Framework\Cache\CacheInitializer;
use App\Framework\DateTime\Clock;
use App\Framework\DateTime\SystemClock;
use App\Framework\DI\Container;
use App\Framework\DI\ContainerCompiler;
use App\Framework\DI\DefaultContainer;
use App\Framework\DI\DependencyResolver;
use App\Framework\Discovery\DiscoveryServiceBootstrapper;
use App\Framework\Http\ResponseEmitter;
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,
) {
}
/**
* Bootstrap container with intelligent compilation strategy
*/
public function bootstrap(
string $basePath,
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;
// }
// Fallback to fresh container bootstrap
return $this->bootstrapFreshContainer($basePath, $collector);
}
/**
* 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());
// TEMPORARY FIX: Manual RequestFactory binding until Discovery issue is resolved
$container->singleton(\App\Framework\Http\Request::class, function ($container) {
error_log("ContainerBootstrapper: Creating Request singleton");
// Get Cache from container (it was just registered above)
$frameworkCache = $container->get(\App\Framework\Cache\Cache::class);
$parserCache = new \App\Framework\Http\Parser\ParserCache($frameworkCache);
$parser = new \App\Framework\Http\Parser\HttpRequestParser($parserCache);
$factory = new \App\Framework\Http\RequestFactory($parser);
error_log("ContainerBootstrapper: About to call factory->createFromGlobals()");
$request = $factory->createFromGlobals();
error_log("ContainerBootstrapper: Request created successfully");
return $request;
});
}
/**
* Compile container asynchronously for next request
*/
private function compileContainerAsync(DefaultContainer $container): void
{
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();
}
/**
* Clear compiled container cache files
*/
private function clearCompiledCache(string $compiledPath): void
{
try {
// Delete the compiled container file
if (file_exists($compiledPath)) {
@unlink($compiledPath);
}
// 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);
}
}
}
} catch (\Throwable $e) {
// Ignore cache cleanup errors, just log them
error_log("Error clearing compiled cache: " . $e->getMessage());
}
}
}