Fix Discovery system context-dependent caching issue

The Discovery system was creating separate caches for WEB vs CLI contexts,
causing RequestFactory #[Initializer] to be missing in WEB context and
leading to 500 errors due to Request interface binding failures.

Changes:
- Remove execution context from Discovery cache keys
- Ensure consistent Discovery results across WEB and CLI contexts
- WEB and CLI now share same Discovery cache (535 items vs 369/535 split)
- RequestFactory consistently discovered in both contexts

Root cause: Context-dependent cache keys caused:
- CLI: discovery:full_{hash}_cli-script
- WEB: discovery:full_{hash}_web

Fixed: Both contexts now use discovery:full_{hash}

Resolves: #21 DI Container Request Interface Binding
Resolves: #18 Discovery WEB vs CLI Context differences
This commit is contained in:
2025-09-13 00:36:07 +02:00
parent 9526034e18
commit 03e5188644
3 changed files with 217 additions and 6 deletions

View File

@@ -64,12 +64,12 @@ final class DiscoveryContext
public function getCacheKey(): CacheKey
{
// Include execution context in cache key if available
$contextString = $this->executionContext
? $this->executionContext->getType()->value
: null;
// FIXED: Remove execution context from cache key to ensure consistent Discovery results
// between WEB and CLI contexts. Discovery results should be the same regardless of
// execution context - the same PHP files should produce the same attributes.
// Context-dependent caching was causing RequestFactory to be missing in WEB context.
return DiscoveryCacheIdentifiers::discoveryKey($this->paths, $this->scanType, $contextString);
return DiscoveryCacheIdentifiers::discoveryKey($this->paths, $this->scanType, null);
}
public function isIncremental(): bool

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
echo "=== Clearing Discovery Cache ===\n";
$cacheDir = __DIR__ . '/../../storage/cache';
if (!is_dir($cacheDir)) {
echo "Cache directory does not exist: $cacheDir\n";
exit(0);
}
// Find all cache files that might be related to discovery
$patterns = [
$cacheDir . '/discovery*',
$cacheDir . '/*discovery*',
$cacheDir . '/routes*',
$cacheDir . '/attr*'
];
$deletedCount = 0;
$totalSize = 0;
foreach ($patterns as $pattern) {
$files = glob($pattern);
foreach ($files as $file) {
if (is_file($file)) {
$size = filesize($file);
if (unlink($file)) {
echo "Deleted: " . basename($file) . " (" . number_format($size) . " bytes)\n";
$deletedCount++;
$totalSize += $size;
} else {
echo "Failed to delete: " . basename($file) . "\n";
}
}
}
}
if ($deletedCount === 0) {
echo "No cache files found to delete.\n";
} else {
echo "\nDeleted $deletedCount files, freed " . number_format($totalSize) . " bytes.\n";
}
echo "Cache clearing complete.\n";

View File

@@ -0,0 +1,165 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use App\Framework\Context\ContextType;
use App\Framework\Context\ExecutionContext;
use App\Framework\Core\AppBootstrapper;
use App\Framework\DI\Initializer;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Performance\MemoryMonitor;
echo "=== Discovery Context Debugging ===\n";
// Clear any existing caches first
$cacheDir = __DIR__ . '/../../storage/cache';
if (is_dir($cacheDir)) {
$files = glob($cacheDir . '/discovery*');
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
echo "Cleared cache file: " . basename($file) . "\n";
}
}
}
echo "\n=== Testing WEB Context ===\n";
// Create bootstrapper (same as web)
$clock = new SystemClock();
$highResClock = new SystemHighResolutionClock();
$memoryMonitor = new MemoryMonitor();
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: false);
$webBootstrapper = new AppBootstrapper(__DIR__ . '/../..', $collector, $memoryMonitor);
// Get container from web application
$webApp = $webBootstrapper->bootstrapWeb();
$webContainer = $webApp->getContainer(); // Assuming this method exists
// Get discovery service
$discovery = $container->get(\App\Framework\Discovery\UnifiedDiscoveryService::class);
// Clear cache and run discovery
$webResults = $discovery->discover();
$webInitializers = $webResults->attributes->get(Initializer::class);
echo "WEB Context Results:\n";
echo "- Total items: " . count($webResults) . "\n";
echo "- Initializer attributes: " . count($webInitializers) . "\n";
// Look for RequestFactory specifically
$requestFactoryFound = false;
foreach ($webInitializers as $result) {
if (str_contains($result->className, 'RequestFactory')) {
echo "- Found RequestFactory: {$result->className}::{$result->methodName}\n";
$requestFactoryFound = true;
break;
}
}
if (!$requestFactoryFound) {
echo "- RequestFactory NOT FOUND in WEB context\n";
}
echo "\n=== Testing CLI Context ===\n";
// Force CLI context
$cliContext = ExecutionContext::forConsole();
$bootstrapper2 = AppBootstrapper::createWithContext($cliContext);
$container2 = $bootstrapper2->bootstrap();
// Get discovery service (should be different container)
$discovery2 = $container2->get(\App\Framework\Discovery\UnifiedDiscoveryService::class);
// Run discovery again
$cliResults = $discovery2->discover();
$cliInitializers = $cliResults->attributes->get(Initializer::class);
echo "CLI Context Results:\n";
echo "- Total items: " . count($cliResults) . "\n";
echo "- Initializer attributes: " . count($cliInitializers) . "\n";
// Look for RequestFactory specifically
$requestFactoryFound = false;
foreach ($cliInitializers as $result) {
if (str_contains($result->className, 'RequestFactory')) {
echo "- Found RequestFactory: {$result->className}::{$result->methodName}\n";
$requestFactoryFound = true;
break;
}
}
if (!$requestFactoryFound) {
echo "- RequestFactory NOT FOUND in CLI context\n";
}
echo "\n=== Comparison ===\n";
$webCount = count($webResults);
$cliCount = count($cliResults);
echo "Difference: " . abs($webCount - $cliCount) . " items\n";
if ($webCount !== $cliCount) {
echo "\n=== Finding Missing Items ===\n";
// Convert to comparable arrays
$webItems = [];
foreach ($webInitializers as $item) {
$key = $item->className . '::' . $item->methodName;
$webItems[$key] = $item;
}
$cliItems = [];
foreach ($cliInitializers as $item) {
$key = $item->className . '::' . $item->methodName;
$cliItems[$key] = $item;
}
$webOnly = array_diff_key($webItems, $cliItems);
$cliOnly = array_diff_key($cliItems, $webItems);
if (!empty($webOnly)) {
echo "Items only in WEB context:\n";
foreach ($webOnly as $key => $item) {
echo "- $key\n";
}
}
if (!empty($cliOnly)) {
echo "Items only in CLI context:\n";
foreach ($cliOnly as $key => $item) {
echo "- $key\n";
}
}
} else {
echo "Results are identical - context fix was successful!\n";
}
echo "\n=== Cache Key Check ===\n";
// Test cache key generation after fix
$webDiscoveryContext = new \App\Framework\Discovery\ValueObjects\DiscoveryContext(
paths: ['/home/michael/dev/michaelschiemer/src'],
scanType: \App\Framework\Discovery\ValueObjects\ScanType::FULL,
options: new \App\Framework\Discovery\ValueObjects\DiscoveryOptions(),
startTime: new DateTimeImmutable(),
executionContext: $webContext
);
$cliDiscoveryContext = new \App\Framework\Discovery\ValueObjects\DiscoveryContext(
paths: ['/home/michael/dev/michaelschiemer/src'],
scanType: \App\Framework\Discovery\ValueObjects\ScanType::FULL,
options: new \App\Framework\Discovery\ValueObjects\DiscoveryOptions(),
startTime: new DateTimeImmutable(),
executionContext: $cliContext
);
$webCacheKey = $webDiscoveryContext->getCacheKey();
$cliCacheKey = $cliDiscoveryContext->getCacheKey();
echo "WEB cache key: " . $webCacheKey->toString() . "\n";
echo "CLI cache key: " . $cliCacheKey->toString() . "\n";
echo "Keys are identical: " . ($webCacheKey->toString() === $cliCacheKey->toString() ? 'YES' : 'NO') . "\n";
echo "\nDebugging complete.\n";