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:
48
tests/debug/DebugInitializer.php
Normal file
48
tests/debug/DebugInitializer.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Debug;
|
||||
|
||||
use App\Framework\DI\Container;
|
||||
use App\Framework\DI\Initializer;
|
||||
|
||||
final readonly class DebugInitializer
|
||||
{
|
||||
public function __construct(
|
||||
private Container $container
|
||||
) {
|
||||
}
|
||||
|
||||
#[Initializer]
|
||||
public function debugTest(): void
|
||||
{
|
||||
echo "🎉 DEBUG: DebugInitializer wurde ausgeführt!\n";
|
||||
echo "🎯 DEBUG: Container-Typ: " . get_class($this->container) . "\n";
|
||||
|
||||
// Log to a file to prove execution
|
||||
file_put_contents(
|
||||
__DIR__ . '/initializer-execution.log',
|
||||
date('Y-m-d H:i:s') . " - DebugInitializer executed\n",
|
||||
FILE_APPEND | LOCK_EX
|
||||
);
|
||||
}
|
||||
|
||||
#[Initializer]
|
||||
public function debugService(): \stdClass
|
||||
{
|
||||
echo "🚀 DEBUG: Service-Initializer wurde aufgerufen!\n";
|
||||
|
||||
// Log to a file to prove execution
|
||||
file_put_contents(
|
||||
__DIR__ . '/initializer-execution.log',
|
||||
date('Y-m-d H:i:s') . " - Service-Initializer executed\n",
|
||||
FILE_APPEND | LOCK_EX
|
||||
);
|
||||
|
||||
$service = new \stdClass();
|
||||
$service->debug = 'Created by DebugInitializer';
|
||||
|
||||
return $service;
|
||||
}
|
||||
}
|
||||
119
tests/debug/cache-debug.php
Normal file
119
tests/debug/cache-debug.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// Debug the actual discovery caching issue
|
||||
|
||||
require_once '/var/www/html/vendor/autoload.php';
|
||||
|
||||
echo "🔍 Discovery Cache Debug\n";
|
||||
echo "=======================\n\n";
|
||||
|
||||
use App\Framework\Cache\CacheInitializer;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Cache\DiscoveryCacheIdentifiers;
|
||||
use App\Framework\Discovery\DiscoveryServiceBootstrapper;
|
||||
use App\Framework\Performance\EnhancedPerformanceCollector;
|
||||
use App\Framework\Performance\HighResolutionClock;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
|
||||
try {
|
||||
// Minimal setup
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider('/var/www/html');
|
||||
$clock = new SystemClock();
|
||||
|
||||
// Mock the performance collector properly
|
||||
$highResClock = new HighResolutionClock();
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
$performanceCollector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, true);
|
||||
|
||||
$container->instance(PathProvider::class, $pathProvider);
|
||||
$container->instance(SystemClock::class, $clock);
|
||||
|
||||
echo "1. Setting up cache...\n";
|
||||
$cacheInitializer = new CacheInitializer($performanceCollector, $container);
|
||||
$cache = $cacheInitializer();
|
||||
$container->instance(\App\Framework\Cache\Cache::class, $cache);
|
||||
|
||||
echo " Cache type: " . get_class($cache) . "\n";
|
||||
|
||||
// Check what cache key would be generated
|
||||
$defaultPaths = [$pathProvider->getSourcePath()];
|
||||
$cacheKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($defaultPaths);
|
||||
|
||||
echo "\n2. Discovery cache key info:\n";
|
||||
echo " Source path: " . $pathProvider->getSourcePath() . "\n";
|
||||
echo " Cache key: " . (string)$cacheKey . "\n";
|
||||
|
||||
// Check if cache already has this key
|
||||
echo "\n3. Checking existing cache...\n";
|
||||
$existingResult = $cache->get($cacheKey);
|
||||
echo " Has key in cache: " . ($existingResult->has($cacheKey) ? 'YES' : 'NO') . "\n";
|
||||
|
||||
if ($existingResult->has($cacheKey)) {
|
||||
$existingItem = $existingResult->get($cacheKey);
|
||||
echo " Existing cache hit: " . ($existingItem->isHit ? 'YES' : 'NO') . "\n";
|
||||
echo " Existing cache value type: " . gettype($existingItem->value) . "\n";
|
||||
}
|
||||
|
||||
echo "\n4. Running discovery bootstrap (first time)...\n";
|
||||
$bootstrapper = new DiscoveryServiceBootstrapper($container, $clock);
|
||||
|
||||
$startTime = microtime(true);
|
||||
$registry = $bootstrapper->bootstrap();
|
||||
$firstRunTime = (microtime(true) - $startTime) * 1000;
|
||||
|
||||
echo " First run time: " . number_format($firstRunTime, 2) . "ms\n";
|
||||
echo " Registry empty: " . ($registry->isEmpty() ? 'YES' : 'NO') . "\n";
|
||||
|
||||
// Check cache after first run
|
||||
echo "\n5. Checking cache after first run...\n";
|
||||
$afterFirstResult = $cache->get($cacheKey);
|
||||
echo " Has key after first run: " . ($afterFirstResult->has($cacheKey) ? 'YES' : 'NO') . "\n";
|
||||
|
||||
if ($afterFirstResult->has($cacheKey)) {
|
||||
$afterFirstItem = $afterFirstResult->get($cacheKey);
|
||||
echo " Cache hit after first run: " . ($afterFirstItem->isHit ? 'YES' : 'NO') . "\n";
|
||||
echo " Cache value type after first run: " . gettype($afterFirstItem->value) . "\n";
|
||||
if (is_array($afterFirstItem->value)) {
|
||||
echo " Cache array size: " . count($afterFirstItem->value) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n6. Running discovery bootstrap (second time)...\n";
|
||||
$startTime = microtime(true);
|
||||
$registry2 = $bootstrapper->bootstrap();
|
||||
$secondRunTime = (microtime(true) - $startTime) * 1000;
|
||||
|
||||
echo " Second run time: " . number_format($secondRunTime, 2) . "ms\n";
|
||||
echo " Registry2 empty: " . ($registry2->isEmpty() ? 'YES' : 'NO') . "\n";
|
||||
|
||||
// Performance comparison
|
||||
echo "\n7. Performance analysis:\n";
|
||||
echo " First run: " . number_format($firstRunTime, 2) . "ms\n";
|
||||
echo " Second run: " . number_format($secondRunTime, 2) . "ms\n";
|
||||
|
||||
if ($secondRunTime < $firstRunTime) {
|
||||
$improvement = (($firstRunTime - $secondRunTime) / $firstRunTime) * 100;
|
||||
echo " Improvement: " . number_format($improvement, 1) . "%\n";
|
||||
|
||||
if ($improvement > 50) {
|
||||
echo " ✅ Cache is working well!\n";
|
||||
} else {
|
||||
echo " ⚠️ Cache working but not optimal\n";
|
||||
}
|
||||
} else {
|
||||
echo " ❌ Cache NOT working - second run was slower or same\n";
|
||||
}
|
||||
|
||||
if ($secondRunTime > 500) {
|
||||
echo "\n❌ PROBLEM: Second run > 500ms, cache not effective!\n";
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
echo "\n❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
}
|
||||
138
tests/debug/debug-attribute-discovery.php
Normal file
138
tests/debug/debug-attribute-discovery.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Console\ConsoleCommandMapper;
|
||||
use App\Framework\Core\ValueObjects\ClassName;
|
||||
use App\Framework\Core\ValueObjects\MethodName;
|
||||
use App\Framework\Discovery\Results\AttributeRegistry;
|
||||
use App\Framework\Discovery\ValueObjects\AttributeTarget;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveredAttribute;
|
||||
use App\Framework\Filesystem\FilePath;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
|
||||
echo "=== Debug Attribute Discovery Logic ===" . PHP_EOL;
|
||||
|
||||
// Initialize components
|
||||
$className = ClassName::create('App\\Framework\\Mcp\\Console\\McpServerCommand');
|
||||
$registry = new AttributeRegistry();
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
|
||||
// Mappers
|
||||
$mapper = new ConsoleCommandMapper();
|
||||
$mapperMap = [$mapper->getAttributeClass() => $mapper];
|
||||
|
||||
echo "Target attribute class: " . $mapper->getAttributeClass() . PHP_EOL;
|
||||
echo "Testing class: " . $className->getFullyQualified() . PHP_EOL;
|
||||
|
||||
if (! $className->exists()) {
|
||||
echo "❌ Class does not exist!" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
$reflection = $reflectionProvider->getClass($className);
|
||||
echo "✅ Got reflection for class" . PHP_EOL;
|
||||
|
||||
// Process method attributes (copied from AttributeVisitor logic)
|
||||
$methods = $reflection->getMethods();
|
||||
echo "Found " . count($methods) . " methods" . PHP_EOL;
|
||||
|
||||
foreach ($methods as $method) {
|
||||
echo " Processing method: " . $method->getName() . PHP_EOL;
|
||||
|
||||
$attributes = $method->getAttributes();
|
||||
echo " Found " . count($attributes) . " attributes" . PHP_EOL;
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
$attributeClass = $attribute->getName();
|
||||
echo " Attribute: $attributeClass" . PHP_EOL;
|
||||
|
||||
// Check if we should ignore this attribute
|
||||
$ignoredAttributes = [
|
||||
'Attribute', 'Override', 'AllowDynamicProperties',
|
||||
'ReturnTypeWillChange', 'SensitiveParameter',
|
||||
];
|
||||
|
||||
$shortName = substr($attributeClass, strrpos($attributeClass, '\\') + 1);
|
||||
if (in_array($shortName, $ignoredAttributes, true) ||
|
||||
in_array($attributeClass, $ignoredAttributes, true)) {
|
||||
echo " → Ignored (built-in attribute)" . PHP_EOL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract attribute arguments
|
||||
$arguments = $attribute->getArguments() ?? [];
|
||||
echo " → Arguments: " . json_encode($arguments) . PHP_EOL;
|
||||
|
||||
// Apply mapper if available
|
||||
$additionalData = [];
|
||||
if (isset($mapperMap[$attributeClass])) {
|
||||
echo " → Found mapper for this attribute" . PHP_EOL;
|
||||
|
||||
try {
|
||||
$mapper = $mapperMap[$attributeClass];
|
||||
$attributeInstance = $attribute->newInstance();
|
||||
$mapped = $mapper->map($method, $attributeInstance);
|
||||
$additionalData = $mapped ?? [];
|
||||
echo " → Mapping result: " . json_encode($additionalData) . PHP_EOL;
|
||||
} catch (Exception $e) {
|
||||
echo " → Mapping error: " . $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
echo " → No mapper found for: $attributeClass" . PHP_EOL;
|
||||
}
|
||||
|
||||
// Create DiscoveredAttribute
|
||||
$filePath = FilePath::create(__DIR__ . '/../../src/Framework/Mcp/Console/McpServerCommand.php');
|
||||
|
||||
$discoveredAttribute = new DiscoveredAttribute(
|
||||
className: $className,
|
||||
attributeClass: $attributeClass,
|
||||
target: AttributeTarget::METHOD,
|
||||
methodName: MethodName::create($method->getName()),
|
||||
propertyName: null,
|
||||
arguments: $arguments,
|
||||
filePath: $filePath,
|
||||
additionalData: $additionalData
|
||||
);
|
||||
|
||||
echo " → Created DiscoveredAttribute" . PHP_EOL;
|
||||
|
||||
// Add to registry
|
||||
$registry->add($attributeClass, $discoveredAttribute);
|
||||
echo " → Added to registry" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
echo PHP_EOL . "Final registry state:" . PHP_EOL;
|
||||
echo "Total attributes: " . $registry->count() . PHP_EOL;
|
||||
echo "Available types: " . count($registry->getAllTypes()) . PHP_EOL;
|
||||
|
||||
foreach ($registry->getAllTypes() as $type) {
|
||||
$count = $registry->getCount($type);
|
||||
echo " - $type: $count" . PHP_EOL;
|
||||
|
||||
if ($type === 'App\\Framework\\Console\\ConsoleCommand') {
|
||||
$commands = $registry->get($type);
|
||||
foreach ($commands as $i => $command) {
|
||||
echo " Command $i:" . PHP_EOL;
|
||||
echo " Class: " . $command->className->getFullyQualified() . PHP_EOL;
|
||||
echo " Method: " . ($command->methodName?->toString() ?? 'unknown') . PHP_EOL;
|
||||
|
||||
$instance = $command->createAttributeInstance();
|
||||
if ($instance) {
|
||||
echo " Name: " . $instance->name . PHP_EOL;
|
||||
echo " Description: " . $instance->description . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . PHP_EOL;
|
||||
echo "Stack trace: " . $e->getTraceAsString() . PHP_EOL;
|
||||
}
|
||||
64
tests/debug/debug-attribute-parsing.php
Normal file
64
tests/debug/debug-attribute-parsing.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
echo "=== Testing Attribute Parsing ===" . PHP_EOL;
|
||||
|
||||
// Test the specific MCP Server Command file
|
||||
$mcpServerPath = __DIR__ . '/../../src/Framework/Mcp/Console/McpServerCommand.php';
|
||||
|
||||
if (! file_exists($mcpServerPath)) {
|
||||
echo "❌ MCP Server Command file not found!" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "✅ Found MCP Server Command file" . PHP_EOL;
|
||||
|
||||
// Read and parse the file content
|
||||
$content = file_get_contents($mcpServerPath);
|
||||
|
||||
// Check for ConsoleCommand attribute
|
||||
if (strpos($content, '#[ConsoleCommand') !== false) {
|
||||
echo "✅ ConsoleCommand attribute found in file" . PHP_EOL;
|
||||
} else {
|
||||
echo "❌ ConsoleCommand attribute NOT found in file" . PHP_EOL;
|
||||
}
|
||||
|
||||
// Use reflection to check if the attribute is actually readable
|
||||
require_once $mcpServerPath;
|
||||
|
||||
try {
|
||||
$reflection = new ReflectionClass('App\\Framework\\Mcp\\Console\\McpServerCommand');
|
||||
echo "✅ Class can be reflected: " . $reflection->getName() . PHP_EOL;
|
||||
|
||||
$methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||
echo "Found " . count($methods) . " public methods:" . PHP_EOL;
|
||||
|
||||
foreach ($methods as $method) {
|
||||
echo " Method: " . $method->getName() . PHP_EOL;
|
||||
|
||||
$attributes = $method->getAttributes();
|
||||
echo " Attributes: " . count($attributes) . PHP_EOL;
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
echo " - " . $attribute->getName() . PHP_EOL;
|
||||
|
||||
if ($attribute->getName() === 'App\\Framework\\Console\\ConsoleCommand') {
|
||||
echo " ✅ Found ConsoleCommand attribute!" . PHP_EOL;
|
||||
|
||||
try {
|
||||
$instance = $attribute->newInstance();
|
||||
echo " Name: " . $instance->name . PHP_EOL;
|
||||
echo " Description: " . $instance->description . PHP_EOL;
|
||||
} catch (Exception $e) {
|
||||
echo " ❌ Error creating instance: " . $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error reflecting class: " . $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
74
tests/debug/debug-bit-count.php
Normal file
74
tests/debug/debug-bit-count.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\QrCode\DataMode;
|
||||
use App\Framework\QrCode\ErrorCorrectionLevel;
|
||||
use App\Framework\QrCode\QrCodeVersion;
|
||||
|
||||
// Simulate DataEncoder steps manually
|
||||
$testData = 'otpauth://totp/Test?secret=JBSWY3DPEHPK3PXP';
|
||||
$version = new QrCodeVersion(3);
|
||||
$errorLevel = ErrorCorrectionLevel::L;
|
||||
|
||||
echo "=== Bit Calculation Debug ===\n";
|
||||
echo "Data: '$testData'\n";
|
||||
echo "Length: " . strlen($testData) . " characters\n";
|
||||
echo "Version: " . $version->getVersion() . "\n";
|
||||
echo "Error Level: {$errorLevel->value}\n";
|
||||
echo "Available capacity: " . $version->getDataCapacity($errorLevel) . " bits\n\n";
|
||||
|
||||
// Step 1: Mode detection
|
||||
$mode = DataMode::detectForData($testData);
|
||||
echo "Detected mode: {$mode->value}\n";
|
||||
|
||||
// Step 2: Mode indicator (4 bits)
|
||||
$modeIndicator = match($mode) {
|
||||
DataMode::NUMERIC => '0001',
|
||||
DataMode::ALPHANUMERIC => '0010',
|
||||
DataMode::BYTE => '0100',
|
||||
DataMode::KANJI => '1000',
|
||||
};
|
||||
echo "Mode indicator: '$modeIndicator' (" . strlen($modeIndicator) . " bits)\n";
|
||||
|
||||
// Step 3: Character count indicator
|
||||
// For byte mode, versions 1-9: 8 bits
|
||||
$characterCount = str_pad(decbin(strlen($testData)), 8, '0', STR_PAD_LEFT);
|
||||
echo "Character count: '$characterCount' (" . strlen($characterCount) . " bits) - represents " . strlen($testData) . " chars\n";
|
||||
|
||||
// Step 4: Data encoding (byte mode = 8 bits per char)
|
||||
$dataEncoding = '';
|
||||
for ($i = 0; $i < strlen($testData); $i++) {
|
||||
$byte = ord($testData[$i]);
|
||||
$dataEncoding .= str_pad(decbin($byte), 8, '0', STR_PAD_LEFT);
|
||||
}
|
||||
echo "Data encoding: " . strlen($dataEncoding) . " bits (" . strlen($testData) . " chars × 8 bits)\n";
|
||||
|
||||
// Current total
|
||||
$currentTotal = strlen($modeIndicator) + strlen($characterCount) + strlen($dataEncoding);
|
||||
echo "\nRunning total: $currentTotal bits\n";
|
||||
|
||||
// Step 5: Terminator (up to 4 bits)
|
||||
$capacity = $version->getDataCapacity($errorLevel);
|
||||
$terminatorBits = min(4, $capacity - $currentTotal);
|
||||
echo "Terminator bits to add: $terminatorBits\n";
|
||||
$currentTotal += $terminatorBits;
|
||||
|
||||
// Step 6: Pad to byte boundary
|
||||
$bytePadding = (8 - ($currentTotal % 8)) % 8;
|
||||
echo "Byte padding needed: $bytePadding bits\n";
|
||||
$currentTotal += $bytePadding;
|
||||
|
||||
echo "\nFinal total before codeword padding: $currentTotal bits\n";
|
||||
echo "Available capacity: $capacity bits\n";
|
||||
echo "Fits: " . ($currentTotal <= $capacity ? 'YES' : 'NO') . "\n";
|
||||
|
||||
// The discrepancy test
|
||||
if ($currentTotal > $capacity) {
|
||||
echo "\n⚠️ ERROR: This should not happen - basic encoding exceeds capacity!\n";
|
||||
echo "Something is wrong with our calculation or the actual DataEncoder.\n";
|
||||
} else {
|
||||
echo "\n✅ Basic encoding fits. Problem must be elsewhere.\n";
|
||||
}
|
||||
77
tests/debug/debug-cache-data.php
Normal file
77
tests/debug/debug-cache-data.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once '/var/www/html/vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\CacheInitializer;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DateTime\SystemHighResolutionClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Cache\DiscoveryCacheIdentifiers;
|
||||
use App\Framework\Performance\EnhancedPerformanceCollector;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
|
||||
echo "=== Debugging Cache Data Structure ===\n\n";
|
||||
|
||||
// Setup wie im Web-Container
|
||||
$basePath = '/var/www/html';
|
||||
$pathProvider = new PathProvider($basePath);
|
||||
$clock = new SystemClock();
|
||||
|
||||
// Create cache
|
||||
$highResClock = new SystemHighResolutionClock();
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
$performanceCollector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, false);
|
||||
$container = new DefaultContainer();
|
||||
$cacheInitializer = new CacheInitializer($performanceCollector, $container);
|
||||
$cache = $cacheInitializer();
|
||||
|
||||
// Generate cache key for web context
|
||||
$defaultPaths = [$pathProvider->getSourcePath()];
|
||||
$cacheKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($defaultPaths);
|
||||
|
||||
echo "Checking cache key: {$cacheKey->toString()}\n\n";
|
||||
|
||||
// Check cache
|
||||
$cachedItem = $cache->get($cacheKey);
|
||||
|
||||
if ($cachedItem !== null && $cachedItem->value !== null) {
|
||||
echo "✅ Cache hit!\n";
|
||||
|
||||
if (is_array($cachedItem->value)) {
|
||||
echo "📊 Analyzing array structure...\n";
|
||||
|
||||
// Zeige die Struktur des Arrays
|
||||
foreach ($cachedItem->value as $key => $value) {
|
||||
echo "Key: {$key}\n";
|
||||
echo " Type: " . gettype($value) . "\n";
|
||||
|
||||
if (is_array($value)) {
|
||||
echo " Array keys: " . implode(', ', array_keys($value)) . "\n";
|
||||
|
||||
if ($key === 'routes' && isset($value['routes'])) {
|
||||
echo " Routes count: " . count($value['routes']) . "\n";
|
||||
if (! empty($value['routes'])) {
|
||||
echo " First route keys: " . implode(', ', array_keys($value['routes'][0] ?? [])) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($key === 'attributes' && isset($value['mappings'])) {
|
||||
echo " Mappings count: " . count($value['mappings']) . "\n";
|
||||
if (! empty($value['mappings'])) {
|
||||
echo " Mapping types: " . implode(', ', array_keys($value['mappings'])) . "\n";
|
||||
}
|
||||
}
|
||||
} elseif (is_object($value)) {
|
||||
echo " Object class: " . get_class($value) . "\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "❌ No cache found\n";
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
80
tests/debug/debug-cache-detailed.php
Normal file
80
tests/debug/debug-cache-detailed.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once '/var/www/html/vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\CacheInitializer;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DateTime\SystemHighResolutionClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Cache\DiscoveryCacheIdentifiers;
|
||||
use App\Framework\Performance\EnhancedPerformanceCollector;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
|
||||
echo "=== Detailed Cache Analysis ===\n\n";
|
||||
|
||||
$basePath = '/var/www/html';
|
||||
$pathProvider = new PathProvider($basePath);
|
||||
$clock = new SystemClock();
|
||||
|
||||
$highResClock = new SystemHighResolutionClock();
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
$performanceCollector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, false);
|
||||
$container = new DefaultContainer();
|
||||
$cacheInitializer = new CacheInitializer($performanceCollector, $container);
|
||||
$cache = $cacheInitializer();
|
||||
|
||||
$defaultPaths = [$pathProvider->getSourcePath()];
|
||||
$cacheKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($defaultPaths);
|
||||
|
||||
$cachedItem = $cache->get($cacheKey);
|
||||
|
||||
if ($cachedItem !== null && $cachedItem->value !== null) {
|
||||
echo "✅ Cache found\n";
|
||||
|
||||
$data = $cachedItem->value;
|
||||
|
||||
echo "Cache Value Type: " . gettype($data) . "\n";
|
||||
|
||||
if (is_object($data)) {
|
||||
echo "Object Class: " . get_class($data) . "\n";
|
||||
echo "Object Count: " . $data->count() . "\n";
|
||||
} elseif (is_string($data)) {
|
||||
echo "String Length: " . strlen($data) . "\n";
|
||||
echo "First 200 chars: " . substr($data, 0, 200) . "\n";
|
||||
|
||||
// Try to decode as JSON
|
||||
$decoded = json_decode($data, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE) {
|
||||
echo "✅ Valid JSON detected\n";
|
||||
echo "JSON Keys: " . implode(', ', array_keys($decoded)) . "\n";
|
||||
} else {
|
||||
echo "❌ Not valid JSON\n";
|
||||
}
|
||||
} elseif (is_array($data)) {
|
||||
echo "Array Keys: " . implode(', ', array_keys($data)) . "\n";
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
echo "\n--- $key ---\n";
|
||||
echo "Type: " . gettype($value) . "\n";
|
||||
|
||||
if (is_array($value)) {
|
||||
echo "Array keys: " . implode(', ', array_keys($value)) . "\n";
|
||||
|
||||
if ($key === 'attributes' && isset($value['mappings'])) {
|
||||
echo "Mappings count: " . count($value['mappings']) . "\n";
|
||||
echo "First 3 mappings keys: " . implode(', ', array_slice(array_keys($value['mappings']), 0, 3)) . "\n";
|
||||
}
|
||||
if ($key === 'routes' && isset($value['routes'])) {
|
||||
echo "Routes count: " . count($value['routes']) . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "❌ No cache found\n";
|
||||
}
|
||||
|
||||
echo "\nDone.\n";
|
||||
67
tests/debug/debug-cache-key.php
Normal file
67
tests/debug/debug-cache-key.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Context\ExecutionContext;
|
||||
use App\Framework\Core\PathProvider;
|
||||
|
||||
echo "=== Debug Cache Key Generation ===\n\n";
|
||||
|
||||
$basePath = dirname(__DIR__, 2);
|
||||
$pathProvider = new PathProvider($basePath);
|
||||
$currentContext = ExecutionContext::detect();
|
||||
$contextName = $currentContext->getType()->value;
|
||||
|
||||
echo "Base Path: {$basePath}\n";
|
||||
echo "Context Name: {$contextName}\n";
|
||||
echo "getBasePath(): {$pathProvider->getBasePath()}\n";
|
||||
|
||||
// Mein Cache-Key
|
||||
$myCacheKey = "discovery:discovery_full_" . md5($contextName . ':' . $pathProvider->getBasePath());
|
||||
echo "My Cache Key: {$myCacheKey}\n";
|
||||
|
||||
// Versuche den Cache-Key aus dem Log zu reproduzieren
|
||||
$expectedKey = "discovery:discovery_full_76dba56b323a5c0dc136e63ace86fce6";
|
||||
echo "Expected Key: {$expectedKey}\n";
|
||||
|
||||
echo "Keys Match: " . ($myCacheKey === $expectedKey ? 'YES' : 'NO') . "\n";
|
||||
|
||||
// Versuche verschiedene Variationen
|
||||
$variation1 = "discovery:discovery_full_" . md5($contextName);
|
||||
echo "Variation 1 (context only): {$variation1}\n";
|
||||
|
||||
$variation2 = "discovery:discovery_full_" . md5($pathProvider->getBasePath());
|
||||
echo "Variation 2 (path only): {$variation2}\n";
|
||||
|
||||
$variation3 = "discovery:discovery_full_" . md5($basePath);
|
||||
echo "Variation 3 (base path): {$variation3}\n";
|
||||
|
||||
// Hash-Prüfung für verschiedene Ansätze
|
||||
$targetHash = "76dba56b323a5c0dc136e63ace86fce6";
|
||||
echo "\nTarget Hash: {$targetHash}\n";
|
||||
|
||||
// Mein aktueller Ansatz
|
||||
$defaultPaths = [$basePath . '/src'];
|
||||
$pathsHash = md5(implode('|', $defaultPaths));
|
||||
echo "Current approach: {$pathsHash}\n";
|
||||
|
||||
// Teste getSourcePath()
|
||||
echo "getSourcePath(): {$pathProvider->getSourcePath()}\n";
|
||||
|
||||
// Teste verschiedene Path-Kombinationen
|
||||
$testCombinations = [
|
||||
[$pathProvider->getSourcePath()],
|
||||
[$basePath . '/src'],
|
||||
[$basePath . '/src', $basePath . '/app'],
|
||||
['/src'],
|
||||
['src'],
|
||||
[$basePath],
|
||||
];
|
||||
|
||||
foreach ($testCombinations as $i => $paths) {
|
||||
$hash = md5(implode('|', $paths));
|
||||
$match = ($hash === $targetHash) ? '✅ MATCH!' : '';
|
||||
echo "Test $i (" . implode('|', $paths) . "): {$hash} {$match}\n";
|
||||
}
|
||||
33
tests/debug/debug-capacities.php
Normal file
33
tests/debug/debug-capacities.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\QrCode\ErrorCorrectionLevel;
|
||||
use App\Framework\QrCode\QrCodeVersion;
|
||||
use App\Framework\QrCode\ReedSolomon\ReedSolomonEncoder;
|
||||
|
||||
echo "=== Capacity Comparison ===\n\n";
|
||||
|
||||
for ($version = 1; $version <= 5; $version++) {
|
||||
$qrVersion = new QrCodeVersion($version);
|
||||
|
||||
echo "Version $version:\n";
|
||||
|
||||
foreach ([ErrorCorrectionLevel::L, ErrorCorrectionLevel::M] as $errorLevel) {
|
||||
$qrCapacity = $qrVersion->getDataCapacity($errorLevel);
|
||||
$rsCapacity = ReedSolomonEncoder::getDataCapacity($version, $errorLevel->value);
|
||||
|
||||
echo " {$errorLevel->value}: QrCodeVersion={$qrCapacity} bits (" . ($qrCapacity / 8) . " bytes), ";
|
||||
echo "ReedSolomon={$rsCapacity} codewords\n";
|
||||
|
||||
// Check if they match when converted
|
||||
if (($qrCapacity / 8) != $rsCapacity) {
|
||||
echo " ❌ MISMATCH: " . ($qrCapacity / 8) . " bytes vs $rsCapacity codewords\n";
|
||||
} else {
|
||||
echo " ✅ MATCH\n";
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
61
tests/debug/debug-capacity.php
Normal file
61
tests/debug/debug-capacity.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\QrCode\DataEncoder;
|
||||
use App\Framework\QrCode\DataMode;
|
||||
use App\Framework\QrCode\ErrorCorrectionLevel;
|
||||
use App\Framework\QrCode\QrCodeVersion;
|
||||
|
||||
// Debug capacity calculation
|
||||
$testData = 'otpauth://totp/Test?secret=JBSWY3DPEHPK3PXP';
|
||||
echo "Testing data: $testData\n";
|
||||
echo "Length: " . strlen($testData) . " characters\n\n";
|
||||
|
||||
// Test different versions
|
||||
for ($versionNum = 1; $versionNum <= 6; $versionNum++) {
|
||||
$version = new QrCodeVersion($versionNum);
|
||||
|
||||
echo "=== Version $versionNum (Size: " . $version->getModuleCount() . "x" . $version->getModuleCount() . ") ===\n";
|
||||
|
||||
foreach ([ErrorCorrectionLevel::L, ErrorCorrectionLevel::M, ErrorCorrectionLevel::Q, ErrorCorrectionLevel::H] as $errorLevel) {
|
||||
$capacity = $version->getDataCapacity($errorLevel);
|
||||
echo " {$errorLevel->value}: $capacity bits (" . ($capacity / 8) . " bytes)\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
// Manual encoding test
|
||||
echo "=== Manual Encoding Analysis ===\n";
|
||||
$mode = DataMode::detectForData($testData);
|
||||
echo "Detected mode: {$mode->value}\n";
|
||||
|
||||
$encoder = new DataEncoder();
|
||||
$version = new QrCodeVersion(3);
|
||||
$errorLevel = ErrorCorrectionLevel::L;
|
||||
|
||||
echo "\nStep-by-step encoding for Version 3, Level L:\n";
|
||||
echo "Capacity: " . $version->getDataCapacity($errorLevel) . " bits\n";
|
||||
|
||||
// Manual bit calculation
|
||||
$modeIndicator = 4; // 4 bits for byte mode
|
||||
$characterCount = 8; // Version 1-9: 8 bits for character count in byte mode
|
||||
$dataLength = strlen($testData) * 8; // Each byte = 8 bits
|
||||
$terminator = 4; // Up to 4 terminator bits
|
||||
$paddingToByte = 7; // Up to 7 bits to pad to byte boundary
|
||||
|
||||
$totalBits = $modeIndicator + $characterCount + $dataLength + $terminator + $paddingToByte;
|
||||
echo "Manual calculation:\n";
|
||||
echo " Mode indicator: $modeIndicator bits\n";
|
||||
echo " Character count: $characterCount bits\n";
|
||||
echo " Data payload: $dataLength bits (" . strlen($testData) . " chars × 8 bits)\n";
|
||||
echo " Terminator: $terminator bits (max)\n";
|
||||
echo " Byte padding: $paddingToByte bits (max)\n";
|
||||
echo " Total (worst case): $totalBits bits\n";
|
||||
echo " Actual needed: ~" . ($modeIndicator + $characterCount + $dataLength + 4) . " bits\n";
|
||||
|
||||
$actualCapacity = $version->getDataCapacity($errorLevel);
|
||||
echo " Available capacity: $actualCapacity bits\n";
|
||||
echo " Should fit: " . ($actualCapacity >= ($modeIndicator + $characterCount + $dataLength + 4) ? "YES" : "NO") . "\n";
|
||||
136
tests/debug/debug-console-commands.php
Normal file
136
tests/debug/debug-console-commands.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Console\ConsoleApplication;
|
||||
use App\Framework\Core\AppBootstrapper;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DateTime\SystemHighResolutionClock;
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Performance\EnhancedPerformanceCollector;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
|
||||
// Create bootstrapper
|
||||
$clock = new SystemClock();
|
||||
$highResClock = new SystemHighResolutionClock();
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: false);
|
||||
$bootstrapper = new AppBootstrapper(__DIR__ . '/../..', $collector, $memoryMonitor);
|
||||
|
||||
try {
|
||||
echo "=== Debugging Console Command Discovery ===" . PHP_EOL;
|
||||
|
||||
// Bootstrap console
|
||||
$consoleApp = $bootstrapper->bootstrapConsole();
|
||||
|
||||
// Get container via reflection
|
||||
$reflection = new ReflectionClass($consoleApp);
|
||||
$containerProperty = $reflection->getProperty('container');
|
||||
$containerProperty->setAccessible(true);
|
||||
$container = $containerProperty->getValue($consoleApp);
|
||||
|
||||
if (! $container->has(DiscoveryRegistry::class)) {
|
||||
echo '❌ DiscoveryRegistry nicht im Container gefunden!' . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$registry = $container->get(DiscoveryRegistry::class);
|
||||
|
||||
// Check for ConsoleCommand attributes with different variations
|
||||
$possibleKeys = [
|
||||
'App\Framework\Console\ConsoleCommand',
|
||||
'ConsoleCommand',
|
||||
'App\\Framework\\Console\\ConsoleCommand',
|
||||
];
|
||||
|
||||
echo "Available attribute classes:" . PHP_EOL;
|
||||
foreach ($registry->attributes->getAllTypes() as $attrClass) {
|
||||
echo " - $attrClass" . PHP_EOL;
|
||||
}
|
||||
echo PHP_EOL;
|
||||
|
||||
$foundCommands = [];
|
||||
foreach ($possibleKeys as $key) {
|
||||
$commands = $registry->attributes->get($key) ?? [];
|
||||
if (! empty($commands)) {
|
||||
$foundCommands[$key] = $commands;
|
||||
echo "Found " . count($commands) . " commands with key '$key'" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if MCP server command is found specifically
|
||||
$mcpServerFound = false;
|
||||
foreach ($foundCommands as $key => $commands) {
|
||||
foreach ($commands as $attr) {
|
||||
if ($attr instanceof App\Framework\Discovery\ValueObjects\DiscoveredAttribute) {
|
||||
if ($attr->className->getFullyQualified() === 'App\\Framework\\Mcp\\Console\\McpServerCommand') {
|
||||
$mcpServerFound = true;
|
||||
echo "✅ MCP Server command found!" . PHP_EOL;
|
||||
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! $mcpServerFound) {
|
||||
echo "❌ MCP Server command NOT found!" . PHP_EOL;
|
||||
}
|
||||
|
||||
if (empty($foundCommands)) {
|
||||
echo "❌ No console commands found with any key!" . PHP_EOL;
|
||||
|
||||
// Try to find any attributes that might be console commands
|
||||
echo "Searching for any method-level attributes..." . PHP_EOL;
|
||||
foreach ($registry->attributes->getAllTypes() as $attrClass) {
|
||||
$attrs = $registry->attributes->get($attrClass);
|
||||
foreach ($attrs as $attr) {
|
||||
if ($attr instanceof App\Framework\Discovery\ValueObjects\DiscoveredAttribute
|
||||
&& $attr->target === App\Framework\Discovery\ValueObjects\AttributeTarget::METHOD) {
|
||||
echo " Found method attribute: $attrClass on " .
|
||||
$attr->className->getFullyQualified() . "::" .
|
||||
($attr->methodName?->toString() ?? 'unknown') . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($foundCommands as $key => $commands) {
|
||||
echo PHP_EOL . "=== Commands found with key '$key' ===" . PHP_EOL;
|
||||
foreach ($commands as $i => $attr) {
|
||||
echo "Command #$i:" . PHP_EOL;
|
||||
if ($attr instanceof App\Framework\Discovery\ValueObjects\DiscoveredAttribute) {
|
||||
echo " Class: " . $attr->className->getFullyQualified() . PHP_EOL;
|
||||
echo " Method: " . ($attr->methodName?->toString() ?? 'null') . PHP_EOL;
|
||||
echo " Arguments: " . json_encode($attr->arguments) . PHP_EOL;
|
||||
echo " Additional Data: " . json_encode($attr->additionalData) . PHP_EOL;
|
||||
|
||||
// Try to create the attribute instance
|
||||
$instance = $attr->createAttributeInstance();
|
||||
if ($instance) {
|
||||
echo " Command Name: " . ($instance->name ?? 'N/A') . PHP_EOL;
|
||||
echo " Description: " . ($instance->description ?? 'N/A') . PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
echo " Raw data: " . print_r($attr, true) . PHP_EOL;
|
||||
}
|
||||
echo PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any commands are registered in the ConsoleApplication
|
||||
$commandsProperty = $reflection->getProperty('commands');
|
||||
$commandsProperty->setAccessible(true);
|
||||
$registeredCommands = $commandsProperty->getValue($consoleApp);
|
||||
|
||||
echo "Commands registered in ConsoleApplication: " . count($registeredCommands) . PHP_EOL;
|
||||
foreach ($registeredCommands as $name => $command) {
|
||||
echo " - $name: " . ($command['description'] ?? 'No description') . PHP_EOL;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo '❌ Error: ' . $e->getMessage() . PHP_EOL;
|
||||
echo 'Trace: ' . $e->getTraceAsString() . PHP_EOL;
|
||||
}
|
||||
108
tests/debug/debug-discovery-deep.php
Normal file
108
tests/debug/debug-discovery-deep.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
|
||||
echo "=== Deep Discovery Debugging ===" . PHP_EOL;
|
||||
|
||||
// Create dependencies
|
||||
$basePath = __DIR__ . '/../..';
|
||||
$pathProvider = new PathProvider($basePath);
|
||||
$cache = new GeneralCache();
|
||||
$clock = new SystemClock();
|
||||
$container = new DefaultContainer();
|
||||
|
||||
// Create discovery service
|
||||
$factory = new DiscoveryServiceFactory(
|
||||
$container,
|
||||
$pathProvider,
|
||||
$cache,
|
||||
$clock
|
||||
);
|
||||
|
||||
// Create discovery service for development
|
||||
$discoveryService = $factory->createForDevelopment();
|
||||
|
||||
echo "Source path: " . $pathProvider->getSourcePath() . PHP_EOL;
|
||||
|
||||
// Check if the MCP server command file exists
|
||||
$mcpServerPath = $basePath . '/src/Framework/Mcp/Console/McpServerCommand.php';
|
||||
echo "MCP Server Command file exists: " . (file_exists($mcpServerPath) ? 'YES' : 'NO') . PHP_EOL;
|
||||
|
||||
if (file_exists($mcpServerPath)) {
|
||||
echo "MCP Server Command file path: " . $mcpServerPath . PHP_EOL;
|
||||
|
||||
// Check the file content for the attribute
|
||||
$content = file_get_contents($mcpServerPath);
|
||||
if (strpos($content, '#[ConsoleCommand') !== false) {
|
||||
echo "✅ ConsoleCommand attribute found in file content" . PHP_EOL;
|
||||
} else {
|
||||
echo "❌ ConsoleCommand attribute NOT found in file content" . PHP_EOL;
|
||||
}
|
||||
|
||||
// Check if it uses the correct namespace
|
||||
if (strpos($content, 'use App\\Framework\\Console\\ConsoleCommand;') !== false) {
|
||||
echo "✅ ConsoleCommand use statement found" . PHP_EOL;
|
||||
} else {
|
||||
echo "❌ ConsoleCommand use statement NOT found" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
echo PHP_EOL . "Running discovery..." . PHP_EOL;
|
||||
|
||||
// Run discovery and get results
|
||||
$results = $discoveryService->discover();
|
||||
|
||||
echo "Total attributes discovered: " . $results->attributes->count() . PHP_EOL;
|
||||
echo "Available attribute types: " . count($results->attributes->getAllTypes()) . PHP_EOL;
|
||||
|
||||
// Check specifically for ConsoleCommand attributes
|
||||
$consoleCommands = $results->attributes->get('App\\Framework\\Console\\ConsoleCommand');
|
||||
echo "ConsoleCommand attributes found: " . count($consoleCommands) . PHP_EOL;
|
||||
|
||||
if (! empty($consoleCommands)) {
|
||||
foreach ($consoleCommands as $i => $command) {
|
||||
echo " Command $i: " . $command->className->getFullyQualified() .
|
||||
"::" . ($command->methodName?->toString() ?? 'unknown') . PHP_EOL;
|
||||
|
||||
$instance = $command->createAttributeInstance();
|
||||
if ($instance) {
|
||||
echo " Name: " . $instance->name . PHP_EOL;
|
||||
echo " Description: " . $instance->description . PHP_EOL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "No ConsoleCommand attributes discovered!" . PHP_EOL;
|
||||
|
||||
// Check if the specific class was discovered at all
|
||||
$allClasses = [];
|
||||
foreach ($results->attributes->getAllTypes() as $attrType) {
|
||||
$attrs = $results->attributes->get($attrType);
|
||||
foreach ($attrs as $attr) {
|
||||
$className = $attr->className->getFullyQualified();
|
||||
if (! in_array($className, $allClasses)) {
|
||||
$allClasses[] = $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array('App\\Framework\\Mcp\\Console\\McpServerCommand', $allClasses)) {
|
||||
echo "✅ McpServerCommand class was discovered with other attributes" . PHP_EOL;
|
||||
} else {
|
||||
echo "❌ McpServerCommand class was NOT discovered at all" . PHP_EOL;
|
||||
}
|
||||
|
||||
echo "Classes discovered: " . count($allClasses) . PHP_EOL;
|
||||
if (count($allClasses) < 20) {
|
||||
foreach ($allClasses as $className) {
|
||||
echo " - $className" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
115
tests/debug/debug-encoding-steps.php
Normal file
115
tests/debug/debug-encoding-steps.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\QrCode\DataEncoder;
|
||||
use App\Framework\QrCode\DataMode;
|
||||
use App\Framework\QrCode\ErrorCorrectionLevel;
|
||||
use App\Framework\QrCode\QrCodeVersion;
|
||||
|
||||
// Debug actual encoding steps by modifying DataEncoder temporarily
|
||||
class DebugDataEncoder extends DataEncoder
|
||||
{
|
||||
public function debugEncode(string $data, QrCodeVersion $version, ErrorCorrectionLevel $errorLevel): array
|
||||
{
|
||||
echo "=== Debug Encoding Steps ===\n";
|
||||
echo "Data: '$data' (length: " . strlen($data) . ")\n";
|
||||
echo "Version: " . $version->getVersion() . "\n";
|
||||
echo "Error Level: {$errorLevel->value}\n";
|
||||
echo "Capacity: " . $version->getDataCapacity($errorLevel) . " bits\n\n";
|
||||
|
||||
// Step 1: Create data bit stream
|
||||
$mode = DataMode::detectForData($data);
|
||||
echo "1. Mode detection: {$mode->value}\n";
|
||||
|
||||
$bitStream = '';
|
||||
|
||||
// Mode indicator
|
||||
$modeIndicator = $this->addModeIndicator($mode);
|
||||
$bitStream .= $modeIndicator;
|
||||
echo "2. Mode indicator: '$modeIndicator' (" . strlen($modeIndicator) . " bits)\n";
|
||||
|
||||
// Character count
|
||||
$charCount = $this->addCharacterCountIndicator($data, $mode, $version);
|
||||
$bitStream .= $charCount;
|
||||
echo "3. Character count: '$charCount' (" . strlen($charCount) . " bits)\n";
|
||||
|
||||
// Data encoding
|
||||
$encodedData = $this->encodeData($data, $mode);
|
||||
$bitStream .= $encodedData;
|
||||
echo "4. Encoded data: " . strlen($encodedData) . " bits\n";
|
||||
echo " Bitstream so far: " . strlen($bitStream) . " bits\n";
|
||||
|
||||
// Terminator
|
||||
$beforeTerminator = $bitStream;
|
||||
$bitStream .= $this->addTerminator($bitStream, $version, $errorLevel);
|
||||
$terminatorAdded = strlen($bitStream) - strlen($beforeTerminator);
|
||||
echo "5. Terminator: $terminatorAdded bits added\n";
|
||||
|
||||
// Pad to byte length
|
||||
$beforePadding = $bitStream;
|
||||
$bitStream = $this->padToByteLength($bitStream);
|
||||
$bytePadding = strlen($bitStream) - strlen($beforePadding);
|
||||
echo "6. Byte padding: $bytePadding bits added\n";
|
||||
echo " Bitstream length: " . strlen($bitStream) . " bits\n";
|
||||
|
||||
// Final padding
|
||||
try {
|
||||
$beforeFinalPadding = $bitStream;
|
||||
$bitStream = $this->addPadding($bitStream, $version, $errorLevel);
|
||||
$finalPadding = strlen($bitStream) - strlen($beforeFinalPadding);
|
||||
echo "7. Final padding: $finalPadding bits added\n";
|
||||
echo "8. Final bitstream: " . strlen($bitStream) . " bits\n";
|
||||
echo " Capacity: " . $version->getDataCapacity($errorLevel) . " bits\n";
|
||||
echo " SUCCESS!\n";
|
||||
|
||||
return [];
|
||||
} catch (Exception $e) {
|
||||
echo "7. ERROR in final padding: " . $e->getMessage() . "\n";
|
||||
echo " Current length: " . strlen($bitStream) . " bits\n";
|
||||
echo " Required capacity: " . $version->getDataCapacity($errorLevel) . " bits\n";
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Make protected methods public for debugging
|
||||
public function addModeIndicator(DataMode $mode): string
|
||||
{
|
||||
return parent::addModeIndicator($mode);
|
||||
}
|
||||
|
||||
public function addCharacterCountIndicator(string $data, DataMode $mode, QrCodeVersion $version): string
|
||||
{
|
||||
return parent::addCharacterCountIndicator($data, $mode, $version);
|
||||
}
|
||||
|
||||
public function encodeData(string $data, DataMode $mode): string
|
||||
{
|
||||
return parent::encodeData($data, $mode);
|
||||
}
|
||||
|
||||
public function addTerminator(string $bitStream, QrCodeVersion $version, ErrorCorrectionLevel $errorLevel): string
|
||||
{
|
||||
return parent::addTerminator($bitStream, $version, $errorLevel);
|
||||
}
|
||||
|
||||
public function padToByteLength(string $bitStream): string
|
||||
{
|
||||
return parent::padToByteLength($bitStream);
|
||||
}
|
||||
|
||||
public function addPadding(string $bitStream, QrCodeVersion $version, ErrorCorrectionLevel $errorLevel): string
|
||||
{
|
||||
return parent::addPadding($bitStream, $version, $errorLevel);
|
||||
}
|
||||
}
|
||||
|
||||
$testData = 'otpauth://totp/Test?secret=JBSWY3DPEHPK3PXP';
|
||||
$version = new QrCodeVersion(3);
|
||||
$errorLevel = ErrorCorrectionLevel::L;
|
||||
|
||||
$debugEncoder = new DebugDataEncoder();
|
||||
$debugEncoder->debugEncode($testData, $version, $errorLevel);
|
||||
83
tests/debug/debug-manual-discovery.php
Normal file
83
tests/debug/debug-manual-discovery.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Console\ConsoleCommandMapper;
|
||||
use App\Framework\Core\ValueObjects\ClassName;
|
||||
use App\Framework\Discovery\ValueObjects\FileContext;
|
||||
use App\Framework\Discovery\Visitors\AttributeVisitor;
|
||||
use App\Framework\Filesystem\File;
|
||||
use App\Framework\Filesystem\FileMetadata;
|
||||
use App\Framework\Filesystem\FilePath;
|
||||
|
||||
echo "=== Manual Discovery Test ===" . PHP_EOL;
|
||||
|
||||
// Create the attribute visitor with console command mapper
|
||||
$visitor = new AttributeVisitor([
|
||||
new ConsoleCommandMapper(),
|
||||
]);
|
||||
|
||||
echo "✅ AttributeVisitor created with ConsoleCommandMapper" . PHP_EOL;
|
||||
|
||||
// Test with the MCP Server Command class
|
||||
$className = ClassName::create('App\\Framework\\Mcp\\Console\\McpServerCommand');
|
||||
$filePathStr = __DIR__ . '/../../src/Framework/Mcp/Console/McpServerCommand.php';
|
||||
$filePath = FilePath::create($filePathStr);
|
||||
|
||||
// Create file metadata
|
||||
$stat = stat($filePathStr);
|
||||
$metadata = new FileMetadata(
|
||||
path: $filePath,
|
||||
size: $stat['size'] ?? 0,
|
||||
lastModified: $stat['mtime'] ?? time()
|
||||
);
|
||||
|
||||
$file = new File($filePath, $metadata);
|
||||
$context = new FileContext($file, $filePath);
|
||||
|
||||
echo "Testing class: " . $className->getFullyQualified() . PHP_EOL;
|
||||
echo "File path: " . $filePath->toString() . PHP_EOL;
|
||||
|
||||
try {
|
||||
// Visit the class to discover attributes
|
||||
$visitor->visitClass($className, $context);
|
||||
|
||||
// Get the discovered attributes
|
||||
$registry = $visitor->getRegistry();
|
||||
$consoleCommands = $registry->get('App\\Framework\\Console\\ConsoleCommand');
|
||||
|
||||
echo "ConsoleCommand attributes found: " . count($consoleCommands) . PHP_EOL;
|
||||
|
||||
if (! empty($consoleCommands)) {
|
||||
foreach ($consoleCommands as $i => $command) {
|
||||
echo "Command $i:" . PHP_EOL;
|
||||
echo " Class: " . $command->className->getFullyQualified() . PHP_EOL;
|
||||
echo " Method: " . ($command->methodName?->toString() ?? 'unknown') . PHP_EOL;
|
||||
echo " Arguments: " . json_encode($command->arguments) . PHP_EOL;
|
||||
|
||||
$instance = $command->createAttributeInstance();
|
||||
if ($instance) {
|
||||
echo " Name: " . $instance->name . PHP_EOL;
|
||||
echo " Description: " . $instance->description . PHP_EOL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "❌ No ConsoleCommand attributes found!" . PHP_EOL;
|
||||
|
||||
// Check what attributes were found
|
||||
$allTypes = $registry->getAllTypes();
|
||||
echo "Available attribute types: " . count($allTypes) . PHP_EOL;
|
||||
foreach ($allTypes as $type) {
|
||||
$count = $registry->getCount($type);
|
||||
echo " - $type: $count" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
echo "Total attributes discovered: " . $registry->count() . PHP_EOL;
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error during discovery: " . $e->getMessage() . PHP_EOL;
|
||||
echo "Stack trace: " . $e->getTraceAsString() . PHP_EOL;
|
||||
}
|
||||
89
tests/debug/debug-visitclass.php
Normal file
89
tests/debug/debug-visitclass.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Core\ValueObjects\ClassName;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
|
||||
echo "=== Debug VisitClass Method ===" . PHP_EOL;
|
||||
|
||||
// Test the specific class
|
||||
$className = ClassName::create('App\\Framework\\Mcp\\Console\\McpServerCommand');
|
||||
echo "Testing class: " . $className->getFullyQualified() . PHP_EOL;
|
||||
|
||||
// Test if class exists
|
||||
if (! $className->exists()) {
|
||||
echo "❌ Class does not exist!" . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "✅ Class exists" . PHP_EOL;
|
||||
|
||||
// Test reflection provider
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
|
||||
try {
|
||||
$reflection = $reflectionProvider->getClass($className);
|
||||
echo "✅ Reflection successful" . PHP_EOL;
|
||||
echo "Class name: " . $reflection->getName() . PHP_EOL;
|
||||
|
||||
// Check methods
|
||||
$methods = $reflection->getMethods();
|
||||
echo "Methods found: " . count($methods) . PHP_EOL;
|
||||
|
||||
foreach ($methods as $method) {
|
||||
echo " Method: " . $method->getName() . PHP_EOL;
|
||||
|
||||
$attributes = $method->getAttributes();
|
||||
echo " Attributes: " . count($attributes) . PHP_EOL;
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
echo " - " . $attribute->getName() . PHP_EOL;
|
||||
|
||||
// Try to create instance
|
||||
try {
|
||||
$instance = $attribute->newInstance();
|
||||
if ($instance instanceof App\Framework\Console\ConsoleCommand) {
|
||||
echo " ✅ ConsoleCommand instance created!" . PHP_EOL;
|
||||
echo " Name: " . $instance->name . PHP_EOL;
|
||||
echo " Description: " . $instance->description . PHP_EOL;
|
||||
} else {
|
||||
echo " - Not a ConsoleCommand: " . get_class($instance) . PHP_EOL;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo " ❌ Error creating instance: " . $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now let's see what happens with the ConsoleCommandMapper
|
||||
$mapper = new App\Framework\Console\ConsoleCommandMapper();
|
||||
echo "✅ ConsoleCommandMapper created" . PHP_EOL;
|
||||
echo "Target attribute class: " . $mapper->getAttributeClass() . PHP_EOL;
|
||||
|
||||
// Test mapping for each method
|
||||
foreach ($methods as $method) {
|
||||
$attributes = $method->getAttributes();
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute->getName() === $mapper->getAttributeClass()) {
|
||||
echo "Found matching attribute on method: " . $method->getName() . PHP_EOL;
|
||||
|
||||
$instance = $attribute->newInstance();
|
||||
$mapped = $mapper->map($method, $instance);
|
||||
|
||||
if ($mapped) {
|
||||
echo "✅ Mapping successful:" . PHP_EOL;
|
||||
echo " " . json_encode($mapped, JSON_PRETTY_PRINT) . PHP_EOL;
|
||||
} else {
|
||||
echo "❌ Mapping returned null" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . PHP_EOL;
|
||||
echo "Stack trace: " . $e->getTraceAsString() . PHP_EOL;
|
||||
}
|
||||
87
tests/debug/debug-visitor-process.php
Normal file
87
tests/debug/debug-visitor-process.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Console\ConsoleCommandMapper;
|
||||
use App\Framework\Core\ValueObjects\ClassName;
|
||||
use App\Framework\Discovery\ValueObjects\FileContext;
|
||||
use App\Framework\Discovery\Visitors\AttributeVisitor;
|
||||
use App\Framework\Filesystem\File;
|
||||
use App\Framework\Filesystem\FileMetadata;
|
||||
use App\Framework\Filesystem\FilePath;
|
||||
|
||||
// Create a custom AttributeVisitor with debug output
|
||||
class DebugAttributeVisitor extends AttributeVisitor
|
||||
{
|
||||
public function visitClass(ClassName $className, FileContext $context): void
|
||||
{
|
||||
echo "🔍 visitClass called for: " . $className->getFullyQualified() . PHP_EOL;
|
||||
|
||||
if (! $className->exists()) {
|
||||
echo "❌ Class does not exist!" . PHP_EOL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
echo "✅ Class exists, proceeding with reflection" . PHP_EOL;
|
||||
|
||||
try {
|
||||
parent::visitClass($className, $context);
|
||||
|
||||
// Check what was discovered
|
||||
$registry = $this->getRegistry();
|
||||
$total = $registry->count();
|
||||
echo "✅ visitClass completed. Total attributes discovered: $total" . PHP_EOL;
|
||||
|
||||
$consoleCommands = $registry->get('App\\Framework\\Console\\ConsoleCommand');
|
||||
echo "ConsoleCommand attributes in registry: " . count($consoleCommands) . PHP_EOL;
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error in visitClass: " . $e->getMessage() . PHP_EOL;
|
||||
echo "Stack trace: " . $e->getTraceAsString() . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "=== Debug Visitor Process ===" . PHP_EOL;
|
||||
|
||||
// Create the debug attribute visitor
|
||||
$visitor = new DebugAttributeVisitor([
|
||||
new ConsoleCommandMapper(),
|
||||
]);
|
||||
|
||||
echo "✅ DebugAttributeVisitor created" . PHP_EOL;
|
||||
|
||||
// Test with the MCP Server Command class
|
||||
$className = ClassName::create('App\\Framework\\Mcp\\Console\\McpServerCommand');
|
||||
$filePathStr = __DIR__ . '/../../src/Framework/Mcp/Console/McpServerCommand.php';
|
||||
$filePath = FilePath::create($filePathStr);
|
||||
|
||||
// Create file metadata
|
||||
$stat = stat($filePathStr);
|
||||
$metadata = new FileMetadata(
|
||||
path: $filePath,
|
||||
size: $stat['size'] ?? 0,
|
||||
lastModified: $stat['mtime'] ?? time()
|
||||
);
|
||||
|
||||
$file = new File($filePath, $metadata);
|
||||
$context = new FileContext($file, $filePath);
|
||||
|
||||
echo "Calling visitClass..." . PHP_EOL;
|
||||
|
||||
// Visit the class to discover attributes
|
||||
$visitor->visitClass($className, $context);
|
||||
|
||||
// Final check
|
||||
$registry = $visitor->getRegistry();
|
||||
echo PHP_EOL . "Final results:" . PHP_EOL;
|
||||
echo "Total attributes: " . $registry->count() . PHP_EOL;
|
||||
echo "Attribute types: " . count($registry->getAllTypes()) . PHP_EOL;
|
||||
|
||||
foreach ($registry->getAllTypes() as $type) {
|
||||
$count = $registry->getCount($type);
|
||||
echo " - $type: $count" . PHP_EOL;
|
||||
}
|
||||
78
tests/debug/minimal-discovery-test.php
Normal file
78
tests/debug/minimal-discovery-test.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
echo "🧪 Minimal Discovery Performance Test\n";
|
||||
echo "====================================\n\n";
|
||||
|
||||
// Measure discovery service bootstrap times directly
|
||||
$iterations = 3;
|
||||
$times = [];
|
||||
|
||||
for ($i = 1; $i <= $iterations; $i++) {
|
||||
echo "Run $i: ";
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
// Simulate what happens in the real bootstrap
|
||||
$output = shell_exec('docker exec php php -r "
|
||||
require_once \"/var/www/html/vendor/autoload.php\";
|
||||
use App\\Framework\\Core\\AppBootstrapper;
|
||||
use App\\Framework\\Performance\\PerformanceCollector;
|
||||
use App\\Framework\\Performance\\MemoryMonitor;
|
||||
|
||||
// Simple mock objects
|
||||
class SimplePerformanceCollector implements App\\Framework\\Performance\\Contracts\\PerformanceCollectorInterface {
|
||||
public function startTiming(string \$operation, ?App\\Framework\\Performance\\PerformanceCategory \$category = null): void {}
|
||||
public function endTiming(string \$operation): void {}
|
||||
public function recordMetric(string \$name, float \$value, ?App\\Framework\\Performance\\PerformanceCategory \$category = null): void {}
|
||||
public function getMetrics(): array { return []; }
|
||||
public function clear(): void {}
|
||||
}
|
||||
|
||||
class SimpleMemoryMonitor {
|
||||
public function getCurrentUsage(): int { return memory_get_usage(); }
|
||||
public function getPeakUsage(): int { return memory_get_peak_usage(); }
|
||||
}
|
||||
|
||||
\$start = microtime(true);
|
||||
\$collector = new SimplePerformanceCollector();
|
||||
\$memoryMonitor = new SimpleMemoryMonitor();
|
||||
\$bootstrapper = new AppBootstrapper(\"/var/www/html\", \$collector, \$memoryMonitor);
|
||||
\$app = \$bootstrapper->bootstrapWeb();
|
||||
\$end = microtime(true);
|
||||
|
||||
echo number_format((\$end - \$start) * 1000, 2) . \"ms\";
|
||||
" 2>/dev/null');
|
||||
|
||||
$endTime = microtime(true);
|
||||
$totalTime = ($endTime - $startTime) * 1000;
|
||||
|
||||
// Extract the bootstrap time from output
|
||||
if (preg_match('/(\d+\.?\d*)ms/', $output, $matches)) {
|
||||
$bootstrapTime = (float)$matches[1];
|
||||
echo $bootstrapTime . "ms (bootstrap time)\n";
|
||||
$times[] = $bootstrapTime;
|
||||
} else {
|
||||
echo "ERROR - could not measure\n";
|
||||
echo "Output: " . trim($output) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($times)) {
|
||||
echo "\n📊 Results:\n";
|
||||
echo " Average: " . number_format(array_sum($times) / count($times), 2) . "ms\n";
|
||||
echo " Min: " . number_format(min($times), 2) . "ms\n";
|
||||
echo " Max: " . number_format(max($times), 2) . "ms\n";
|
||||
|
||||
$avgTime = array_sum($times) / count($times);
|
||||
if ($avgTime > 3000) {
|
||||
echo "\n❌ PROBLEM: Bootstrap taking > 3000ms\n";
|
||||
echo "This indicates the Discovery Service cache is not working!\n";
|
||||
} elseif ($avgTime > 500) {
|
||||
echo "\n⚠️ WARNING: Bootstrap taking > 500ms\n";
|
||||
echo "Cache might not be optimal.\n";
|
||||
} else {
|
||||
echo "\n✅ Good: Bootstrap time is acceptable\n";
|
||||
}
|
||||
}
|
||||
69
tests/debug/simple-cache-test.php
Normal file
69
tests/debug/simple-cache-test.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
echo "🧪 Simple Cache Test\n";
|
||||
echo "==================\n\n";
|
||||
|
||||
use App\Framework\Cache\CacheInitializer;
|
||||
use App\Framework\Cache\CacheItem;
|
||||
use App\Framework\Cache\CacheKey;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Performance\EnhancedPerformanceCollector;
|
||||
|
||||
try {
|
||||
// Setup
|
||||
$container = new DefaultContainer();
|
||||
$performanceCollector = new EnhancedPerformanceCollector();
|
||||
$pathProvider = new PathProvider(__DIR__ . '/../..');
|
||||
$clock = new SystemClock();
|
||||
|
||||
$container->instance(PathProvider::class, $pathProvider);
|
||||
$container->instance(EnhancedPerformanceCollector::class, $performanceCollector);
|
||||
$container->instance(SystemClock::class, $clock);
|
||||
|
||||
echo "1. Initializing cache...\n";
|
||||
$cacheInitializer = new CacheInitializer($performanceCollector, $container);
|
||||
$cache = $cacheInitializer();
|
||||
|
||||
echo " Cache class: " . get_class($cache) . "\n";
|
||||
|
||||
// Test basic cache operations
|
||||
echo "\n2. Testing basic cache operations...\n";
|
||||
$testKey = CacheKey::fromString('test_simple');
|
||||
$testData = ['hello' => 'world', 'time' => time()];
|
||||
|
||||
$cacheItem = CacheItem::forSetting(
|
||||
key: $testKey,
|
||||
value: $testData,
|
||||
ttl: Duration::fromMinutes(5)
|
||||
);
|
||||
|
||||
echo " Writing to cache...\n";
|
||||
$writeResult = $cache->set($cacheItem);
|
||||
echo " Write result: " . ($writeResult ? 'SUCCESS' : 'FAILED') . "\n";
|
||||
|
||||
echo " Reading from cache...\n";
|
||||
$readResult = $cache->get($testKey);
|
||||
echo " Read result class: " . get_class($readResult) . "\n";
|
||||
|
||||
if ($readResult->has($testKey)) {
|
||||
$cachedItem = $readResult->get($testKey);
|
||||
echo " Cache hit: " . ($cachedItem->isHit ? 'YES' : 'NO') . "\n";
|
||||
echo " Cached value: " . json_encode($cachedItem->value) . "\n";
|
||||
} else {
|
||||
echo " ❌ Cache miss - key not found\n";
|
||||
}
|
||||
|
||||
echo "\n✅ Test completed successfully!\n";
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
echo "\n❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
152
tests/debug/test-all-initializers.php
Normal file
152
tests/debug/test-all-initializers.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Context\ExecutionContext;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\DI\InitializerMapper;
|
||||
use App\Framework\Discovery\UnifiedDiscoveryService;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Testing ALL Initializer Discovery ===\n\n";
|
||||
|
||||
try {
|
||||
// Setup with correct path
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$srcPath = $projectRoot . '/src';
|
||||
|
||||
echo "Project root: $projectRoot\n";
|
||||
echo "Source path: $srcPath\n";
|
||||
echo "Current execution context: " . ExecutionContext::detect()->getType()->value . "\n\n";
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
$fileSystemService = new FileSystemService();
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
|
||||
// Create cache for discovery
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
// Create InitializerMapper
|
||||
$initializerMapper = new InitializerMapper();
|
||||
|
||||
// Configure discovery for the entire src directory WITHOUT cache
|
||||
$config = new DiscoveryConfiguration(
|
||||
paths: [$srcPath],
|
||||
attributeMappers: [$initializerMapper],
|
||||
targetInterfaces: [],
|
||||
useCache: false
|
||||
);
|
||||
|
||||
// Create discovery service
|
||||
$discoveryService = new UnifiedDiscoveryService(
|
||||
pathProvider: $pathProvider,
|
||||
cache: $cache,
|
||||
clock: $clock,
|
||||
reflectionProvider: $reflectionProvider,
|
||||
configuration: $config,
|
||||
attributeMappers: [$initializerMapper],
|
||||
targetInterfaces: []
|
||||
);
|
||||
|
||||
echo "Starting full discovery scan...\n";
|
||||
|
||||
$registry = $discoveryService->discover();
|
||||
|
||||
echo "Discovery completed!\n\n";
|
||||
|
||||
// Check for Initializers in attribute registry
|
||||
$initializerAttributeClass = 'App\\Framework\\DI\\Initializer';
|
||||
$initializers = $registry->attributes->get($initializerAttributeClass);
|
||||
|
||||
echo "=== All Found Initializers ===\n";
|
||||
echo "Total initializers found: " . count($initializers) . "\n\n";
|
||||
|
||||
if (empty($initializers)) {
|
||||
echo "❌ No initializers found!\n";
|
||||
} else {
|
||||
echo "✅ All found initializers:\n";
|
||||
|
||||
// Group by class for better overview
|
||||
$initializersByClass = [];
|
||||
foreach ($initializers as $mapping) {
|
||||
$className = $mapping->class->getFullyQualified();
|
||||
$methodName = $mapping->method ? $mapping->method->toString() : '(class-level)';
|
||||
|
||||
if (! isset($initializersByClass[$className])) {
|
||||
$initializersByClass[$className] = [];
|
||||
}
|
||||
|
||||
$initializersByClass[$className][] = [
|
||||
'method' => $methodName,
|
||||
'target' => $mapping->target,
|
||||
'return_type' => $mapping->mappedData['return'] ?? 'null',
|
||||
'contexts' => $mapping->mappedData['contexts'] ?? null,
|
||||
'file' => str_replace($projectRoot, '', $mapping->file->toString()),
|
||||
];
|
||||
}
|
||||
|
||||
// Sort by class name for consistent output
|
||||
ksort($initializersByClass);
|
||||
|
||||
foreach ($initializersByClass as $className => $methods) {
|
||||
$shortName = substr(strrchr($className, '\\'), 1);
|
||||
echo "\n📦 $shortName\n";
|
||||
echo " Class: $className\n";
|
||||
|
||||
foreach ($methods as $method) {
|
||||
echo " 🔧 Method: {$method['method']}\n";
|
||||
echo " Return: {$method['return_type']}\n";
|
||||
echo " Target: {$method['target']}\n";
|
||||
|
||||
if ($method['contexts']) {
|
||||
echo " Contexts: " . json_encode($method['contexts']) . "\n";
|
||||
}
|
||||
|
||||
echo " File: {$method['file']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n=== Summary ===\n";
|
||||
echo "Classes with initializers: " . count($initializersByClass) . "\n";
|
||||
echo "Total initializer methods: " . count($initializers) . "\n";
|
||||
|
||||
// Check for specific missing initializers
|
||||
$expectedInitializers = [
|
||||
'RequestFactory',
|
||||
'CacheInitializer',
|
||||
'LoggerInitializer',
|
||||
'DatabaseInitializer',
|
||||
'SessionInitializer',
|
||||
];
|
||||
|
||||
echo "\n=== Expected Initializers Check ===\n";
|
||||
foreach ($expectedInitializers as $expected) {
|
||||
$found = false;
|
||||
foreach ($initializersByClass as $className => $methods) {
|
||||
if (str_contains($className, $expected)) {
|
||||
$found = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
echo "- $expected: " . ($found ? "✅ Found" : "❌ Missing") . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
59
tests/debug/test-cache-hit.php
Normal file
59
tests/debug/test-cache-hit.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\CacheInitializer;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DateTime\SystemHighResolutionClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Cache\DiscoveryCacheIdentifiers;
|
||||
use App\Framework\Performance\EnhancedPerformanceCollector;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
|
||||
echo "=== Testing Cache Hit Detection ===\n\n";
|
||||
|
||||
$basePath = dirname(__DIR__, 2);
|
||||
$pathProvider = new PathProvider($basePath);
|
||||
$clock = new SystemClock();
|
||||
|
||||
// Create cache
|
||||
$highResClock = new SystemHighResolutionClock();
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
$performanceCollector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, false);
|
||||
$container = new DefaultContainer();
|
||||
$cacheInitializer = new CacheInitializer($performanceCollector, $container);
|
||||
$cache = $cacheInitializer();
|
||||
|
||||
// Generate the exact cache key using the new CacheIdentifier system
|
||||
$defaultPaths = [$pathProvider->getSourcePath()];
|
||||
$cacheKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($defaultPaths);
|
||||
|
||||
echo "Checking cache key: {$cacheKey->toString()}\n";
|
||||
echo "Source path: {$pathProvider->getSourcePath()}\n";
|
||||
|
||||
// Check cache
|
||||
$cachedItem = $cache->get($cacheKey);
|
||||
|
||||
if ($cachedItem !== null) {
|
||||
echo "✅ CACHE HIT! Registry found in cache.\n";
|
||||
if ($cachedItem->value !== null) {
|
||||
echo "Registry type: " . get_class($cachedItem->value) . "\n";
|
||||
} else {
|
||||
echo "⚠️ Registry value is null - deserialization issue?\n";
|
||||
}
|
||||
|
||||
if ($cachedItem->value instanceof \App\Framework\Discovery\Results\DiscoveryRegistry) {
|
||||
$registry = $cachedItem->value;
|
||||
echo "Routes count: " . $registry->routes->count() . "\n";
|
||||
echo "Attributes count: " . count($registry->attributes->getAllTypes()) . "\n";
|
||||
echo "Interfaces count: " . $registry->interfaces->count() . "\n";
|
||||
echo "Templates count: " . $registry->templates->count() . "\n";
|
||||
}
|
||||
} else {
|
||||
echo "❌ CACHE MISS! No registry found in cache.\n";
|
||||
}
|
||||
|
||||
echo "\nDone.\n";
|
||||
111
tests/debug/test-component-scanner.php
Normal file
111
tests/debug/test-component-scanner.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Design\ComponentScanner;
|
||||
use App\Framework\Filesystem\FilePath;
|
||||
|
||||
$scanner = new ComponentScanner();
|
||||
|
||||
// Test with a simple CSS file
|
||||
$testCss = <<<CSS
|
||||
.card {
|
||||
background: white;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: blue;
|
||||
color: white;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #3b82f6;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: green;
|
||||
color: white;
|
||||
}
|
||||
CSS;
|
||||
|
||||
// Write test CSS to a temporary file
|
||||
$tempFile = __DIR__ . '/../tmp/test-components.css';
|
||||
@mkdir(dirname($tempFile), 0777, true);
|
||||
file_put_contents($tempFile, $testCss);
|
||||
|
||||
try {
|
||||
// Scan the test file
|
||||
$registry = $scanner->scanComponents([$tempFile]);
|
||||
|
||||
echo "Total components found: " . $registry->getTotalComponents() . "\n\n";
|
||||
|
||||
$components = $registry->getAllComponents();
|
||||
foreach ($components as $component) {
|
||||
echo "Component: " . $component->name . "\n";
|
||||
echo " - Display Name: " . $component->getDisplayName() . "\n";
|
||||
echo " - Category: " . $component->category->value . "\n";
|
||||
echo " - Pattern: " . $component->pattern->value . "\n";
|
||||
echo " - State: " . $component->state->value . "\n";
|
||||
echo " - Selector: " . $component->selector . "\n";
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
// Now test with real CSS files
|
||||
echo "\n--- Scanning Real CSS Files ---\n\n";
|
||||
|
||||
$cssFiles = [
|
||||
__DIR__ . '/../../resources/css/components/card.css',
|
||||
__DIR__ . '/../../resources/css/components/buttons.css',
|
||||
__DIR__ . '/../../resources/css/components/sidebar.css',
|
||||
];
|
||||
|
||||
$existingFiles = array_filter($cssFiles, 'file_exists');
|
||||
|
||||
if (!empty($existingFiles)) {
|
||||
$realRegistry = $scanner->scanComponents($existingFiles);
|
||||
|
||||
echo "Total components found in real files: " . $realRegistry->getTotalComponents() . "\n\n";
|
||||
|
||||
$realComponents = $realRegistry->getAllComponents();
|
||||
foreach (array_slice($realComponents, 0, 10) as $component) {
|
||||
echo "Component: " . $component->name . "\n";
|
||||
echo " - Display Name: " . $component->getDisplayName() . "\n";
|
||||
echo " - Category: " . $component->category->value . "\n";
|
||||
echo " - Pattern: " . $component->pattern->value . "\n";
|
||||
echo " - File: " . basename($component->filePath) . "\n";
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
// Show category counts
|
||||
echo "\nCategory Distribution:\n";
|
||||
$categoryCounts = $realRegistry->getCategoryCounts();
|
||||
foreach ($categoryCounts as $category => $count) {
|
||||
if ($count > 0) {
|
||||
echo " - $category: $count components\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "No real CSS files found to scan.\n";
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
echo "Error: " . $e->getMessage() . "\n";
|
||||
echo "Trace: " . $e->getTraceAsString() . "\n";
|
||||
} finally {
|
||||
// Clean up temp file
|
||||
@unlink($tempFile);
|
||||
}
|
||||
140
tests/debug/test-context-filtering.php
Normal file
140
tests/debug/test-context-filtering.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// Simulate web context
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
$_SERVER['HTTP_HOST'] = 'localhost';
|
||||
$_SERVER['SERVER_NAME'] = 'localhost';
|
||||
$_SERVER['REQUEST_URI'] = '/';
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Context\ExecutionContext;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\DI\InitializerMapper;
|
||||
use App\Framework\Discovery\UnifiedDiscoveryService;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Testing Context Filtering in Web Context ===\n\n";
|
||||
|
||||
try {
|
||||
// Setup
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$srcPath = $projectRoot . '/src';
|
||||
|
||||
$currentContext = ExecutionContext::detect();
|
||||
echo "Current execution context: " . $currentContext->getType()->value . "\n\n";
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
$fileSystemService = new FileSystemService();
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
|
||||
// Create cache for discovery
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
// Create InitializerMapper
|
||||
$initializerMapper = new InitializerMapper();
|
||||
|
||||
// Configure discovery
|
||||
$config = new DiscoveryConfiguration(
|
||||
paths: [$srcPath],
|
||||
attributeMappers: [$initializerMapper],
|
||||
targetInterfaces: [],
|
||||
useCache: false
|
||||
);
|
||||
|
||||
// Create discovery service
|
||||
$discoveryService = new UnifiedDiscoveryService(
|
||||
pathProvider: $pathProvider,
|
||||
cache: $cache,
|
||||
clock: $clock,
|
||||
reflectionProvider: $reflectionProvider,
|
||||
configuration: $config,
|
||||
attributeMappers: [$initializerMapper],
|
||||
targetInterfaces: []
|
||||
);
|
||||
|
||||
echo "Starting discovery scan...\n";
|
||||
|
||||
$registry = $discoveryService->discover();
|
||||
|
||||
echo "Discovery completed!\n\n";
|
||||
|
||||
// Check for Initializers
|
||||
$initializerAttributeClass = 'App\\Framework\\DI\\Initializer';
|
||||
$initializers = $registry->attributes->get($initializerAttributeClass);
|
||||
|
||||
echo "=== Context Filtering Analysis ===\n";
|
||||
echo "Total initializers found: " . count($initializers) . "\n\n";
|
||||
|
||||
$allowedCount = 0;
|
||||
$blockedCount = 0;
|
||||
$noContextCount = 0;
|
||||
|
||||
foreach ($initializers as $mapping) {
|
||||
$className = $mapping->class->getFullyQualified();
|
||||
$shortName = $mapping->class->getShortName();
|
||||
$initializerData = $mapping->mappedData;
|
||||
|
||||
// Check context filtering logic
|
||||
$isAllowed = true;
|
||||
$contextInfo = "No context restriction";
|
||||
|
||||
if (isset($initializerData['contexts'])) {
|
||||
$allowedContexts = $initializerData['contexts'];
|
||||
|
||||
if ($allowedContexts === null) {
|
||||
$contextInfo = "Null contexts (allowed everywhere)";
|
||||
$noContextCount++;
|
||||
} elseif (is_array($allowedContexts)) {
|
||||
$contextInfo = "Contexts: " . json_encode($allowedContexts);
|
||||
$isAllowed = in_array($currentContext->getType(), $allowedContexts, true);
|
||||
|
||||
if ($isAllowed) {
|
||||
$allowedCount++;
|
||||
} else {
|
||||
$blockedCount++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$noContextCount++;
|
||||
}
|
||||
|
||||
$status = $isAllowed ? "✅ ALLOWED" : "❌ BLOCKED";
|
||||
|
||||
echo "$status $shortName\n";
|
||||
echo " $contextInfo\n";
|
||||
|
||||
// Debug the actual data structure
|
||||
if (isset($initializerData['contexts']) && ! $isAllowed) {
|
||||
echo " DEBUG - Contexts data: " . var_export($initializerData['contexts'], true) . "\n";
|
||||
echo " DEBUG - Current context type: " . var_export($currentContext->getType(), true) . "\n";
|
||||
echo " DEBUG - Context type class: " . get_class($currentContext->getType()) . "\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
echo "=== Summary ===\n";
|
||||
echo "Total: " . count($initializers) . "\n";
|
||||
echo "Allowed: $allowedCount\n";
|
||||
echo "Blocked: $blockedCount\n";
|
||||
echo "No context restriction: $noContextCount\n";
|
||||
echo "Expected to run in WEB context: " . ($allowedCount + $noContextCount) . "\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
159
tests/debug/test-csrf-multipart.php
Normal file
159
tests/debug/test-csrf-multipart.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Test script to verify CSRF token handling in multipart/form-data requests
|
||||
* This simulates what happens when a form is submitted via JavaScript with FormData
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\SmartCache;
|
||||
use App\Framework\Http\Parser\HttpRequestParser;
|
||||
use App\Framework\Http\Parser\ParserCache;
|
||||
|
||||
// Create a mock multipart/form-data request similar to what JavaScript FormData sends
|
||||
function createMultipartRequest(): array
|
||||
{
|
||||
$boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW';
|
||||
$contentType = "multipart/form-data; boundary=$boundary";
|
||||
|
||||
// Simulate multipart body with CSRF tokens
|
||||
$body = "--$boundary\r\n";
|
||||
$body .= "Content-Disposition: form-data; name=\"_form_id\"\r\n\r\n";
|
||||
$body .= "contact_form\r\n";
|
||||
$body .= "--$boundary\r\n";
|
||||
$body .= "Content-Disposition: form-data; name=\"_token\"\r\n\r\n";
|
||||
$body .= "abc123token456\r\n";
|
||||
$body .= "--$boundary\r\n";
|
||||
$body .= "Content-Disposition: form-data; name=\"name\"\r\n\r\n";
|
||||
$body .= "John Doe\r\n";
|
||||
$body .= "--$boundary\r\n";
|
||||
$body .= "Content-Disposition: form-data; name=\"email\"\r\n\r\n";
|
||||
$body .= "john@example.com\r\n";
|
||||
$body .= "--$boundary--\r\n";
|
||||
|
||||
return [
|
||||
'server' => [
|
||||
'REQUEST_METHOD' => 'POST',
|
||||
'REQUEST_URI' => '/contact',
|
||||
'CONTENT_TYPE' => $contentType,
|
||||
'HTTP_HOST' => 'localhost',
|
||||
],
|
||||
'body' => $body,
|
||||
'expected_post' => [
|
||||
'_form_id' => 'contact_form',
|
||||
'_token' => 'abc123token456',
|
||||
'name' => 'John Doe',
|
||||
'email' => 'john@example.com',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// Test 1: Normal multipart parsing (should work with custom parsers)
|
||||
function testMultipartParsing()
|
||||
{
|
||||
echo "=== Test 1: Normal Multipart Parsing ===\n";
|
||||
|
||||
$data = createMultipartRequest();
|
||||
$cache = new ParserCache(new SmartCache(new InMemoryCache()));
|
||||
$parser = new HttpRequestParser($cache);
|
||||
|
||||
try {
|
||||
$request = $parser->parseRequest(
|
||||
method: $data['server']['REQUEST_METHOD'],
|
||||
uri: $data['server']['REQUEST_URI'],
|
||||
server: $data['server'],
|
||||
rawBody: $data['body']
|
||||
);
|
||||
|
||||
echo "✓ Request parsed successfully\n";
|
||||
echo "Method: " . $request->method->value . "\n";
|
||||
echo "Parsed body data: " . json_encode($request->parsedBody->data) . "\n";
|
||||
|
||||
// Check if CSRF tokens are present
|
||||
$formId = $request->parsedBody->get('_form_id');
|
||||
$token = $request->parsedBody->get('_token');
|
||||
|
||||
if ($formId && $token) {
|
||||
echo "✓ CSRF tokens found: form_id='$formId', token='$token'\n";
|
||||
} else {
|
||||
echo "✗ CSRF tokens missing!\n";
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "✗ Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
// Test 2: Simulate empty php://input scenario (this tests our fallback)
|
||||
function testEmptyInputFallback()
|
||||
{
|
||||
echo "=== Test 2: Empty php://input Fallback ===\n";
|
||||
|
||||
// Simulate the scenario where php://input is empty but $_POST has data
|
||||
// This is what actually happens with multipart/form-data
|
||||
global $_POST;
|
||||
$originalPost = $_POST;
|
||||
|
||||
$_POST = [
|
||||
'_form_id' => 'contact_form',
|
||||
'_token' => 'fallback_token_123',
|
||||
'name' => 'Jane Doe',
|
||||
'email' => 'jane@example.com',
|
||||
];
|
||||
|
||||
$cache = new ParserCache(new SmartCache(new InMemoryCache()));
|
||||
$parser = new HttpRequestParser($cache);
|
||||
|
||||
try {
|
||||
$request = $parser->parseRequest(
|
||||
method: 'POST',
|
||||
uri: '/contact',
|
||||
server: [
|
||||
'REQUEST_METHOD' => 'POST',
|
||||
'REQUEST_URI' => '/contact',
|
||||
'CONTENT_TYPE' => 'multipart/form-data; boundary=test',
|
||||
'HTTP_HOST' => 'localhost',
|
||||
],
|
||||
rawBody: '' // Empty body simulates php://input being empty
|
||||
);
|
||||
|
||||
echo "✓ Request parsed with empty body\n";
|
||||
echo "Parsed body data: " . json_encode($request->parsedBody->data) . "\n";
|
||||
|
||||
// Check if fallback worked
|
||||
$formId = $request->parsedBody->get('_form_id');
|
||||
$token = $request->parsedBody->get('_token');
|
||||
|
||||
if ($formId === 'contact_form' && $token === 'fallback_token_123') {
|
||||
echo "✓ Fallback to \$_POST worked correctly!\n";
|
||||
} else {
|
||||
echo "✗ Fallback failed: form_id='$formId', token='$token'\n";
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "✗ Error: " . $e->getMessage() . "\n";
|
||||
} finally {
|
||||
$_POST = $originalPost; // Restore original $_POST
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
// Run tests
|
||||
echo "Testing CSRF Token Handling in Multipart Requests\n";
|
||||
echo "================================================\n\n";
|
||||
|
||||
testMultipartParsing();
|
||||
testEmptyInputFallback();
|
||||
|
||||
echo "Test completed!\n";
|
||||
echo "\n";
|
||||
echo "If both tests pass, the CSRF token issue should be resolved.\n";
|
||||
echo "The key improvement is that HttpRequestParser now uses \$_POST as fallback\n";
|
||||
echo "when php://input is empty for multipart/form-data requests.\n";
|
||||
66
tests/debug/test-csrf-simple.php
Normal file
66
tests/debug/test-csrf-simple.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Simple test to verify the CSRF token fix works
|
||||
* Tests the specific scenario where php://input is empty but $_POST has data
|
||||
*/
|
||||
|
||||
// Simulate the exact scenario that was failing
|
||||
echo "=== CSRF Token Fix Test ===\n";
|
||||
|
||||
// Before fix: php://input was empty for multipart/form-data, so tokens were missing
|
||||
// After fix: HttpRequestParser should use $_POST as fallback
|
||||
|
||||
// Simulate what JavaScript FormData sends
|
||||
$_POST = [
|
||||
'_form_id' => 'contact_form',
|
||||
'_token' => 'test_token_123',
|
||||
'name' => 'John Doe',
|
||||
'email' => 'john@example.com',
|
||||
];
|
||||
|
||||
// Test the specific condition that was added to HttpRequestParser
|
||||
$rawBody = ''; // Empty - simulates php://input being empty
|
||||
$contentType = 'multipart/form-data; boundary=test';
|
||||
|
||||
// Test the logic from HttpRequestParser line 177-183
|
||||
if (str_contains($contentType, 'multipart/form-data')) {
|
||||
if (strlen($rawBody) === 0 && ! empty($_POST)) {
|
||||
echo "✓ Condition met: php://input is empty and \$_POST has data\n";
|
||||
echo "✓ Would use \$_POST fallback: " . json_encode($_POST) . "\n";
|
||||
|
||||
// Check if CSRF tokens are present
|
||||
$formId = $_POST['_form_id'] ?? null;
|
||||
$token = $_POST['_token'] ?? null;
|
||||
|
||||
if ($formId && $token) {
|
||||
echo "✓ CSRF tokens found in \$_POST: form_id='$formId', token='$token'\n";
|
||||
echo "✓ CSRF validation should now work!\n";
|
||||
} else {
|
||||
echo "✗ CSRF tokens missing from \$_POST\n";
|
||||
}
|
||||
} else {
|
||||
echo "✗ Fallback condition not met\n";
|
||||
echo " - rawBody length: " . strlen($rawBody) . "\n";
|
||||
echo " - \$_POST empty: " . (empty($_POST) ? 'yes' : 'no') . "\n";
|
||||
}
|
||||
} else {
|
||||
echo "✗ Not multipart/form-data content type\n";
|
||||
}
|
||||
|
||||
echo "\n=== Summary ===\n";
|
||||
echo "The fix adds this logic to HttpRequestParser::parseRequest():\n";
|
||||
echo "- When Content-Type is multipart/form-data\n";
|
||||
echo "- AND php://input is empty (length 0)\n";
|
||||
echo "- AND \$_POST has data\n";
|
||||
echo "- THEN use \$_POST data instead\n";
|
||||
echo "\nThis solves the CSRF token issue because:\n";
|
||||
echo "1. JavaScript FormData sends multipart/form-data\n";
|
||||
echo "2. PHP automatically parses this into \$_POST (making php://input empty)\n";
|
||||
echo "3. Our fallback now captures the CSRF tokens from \$_POST\n";
|
||||
echo "4. CsrfMiddleware can find the tokens in request->parsedBody->data\n";
|
||||
|
||||
// Reset $_POST
|
||||
$_POST = [];
|
||||
115
tests/debug/test-debug-initializer.php
Normal file
115
tests/debug/test-debug-initializer.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\DI\InitializerMapper;
|
||||
use App\Framework\Discovery\UnifiedDiscoveryService;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
use Tests\Debug\DebugInitializer;
|
||||
|
||||
echo "=== Testing Debug Initializer Execution ===\n\n";
|
||||
|
||||
// Clear previous log
|
||||
$logFile = __DIR__ . '/initializer-execution.log';
|
||||
if (file_exists($logFile)) {
|
||||
unlink($logFile);
|
||||
}
|
||||
|
||||
try {
|
||||
// Setup
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
$fileSystemService = new FileSystemService();
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
|
||||
// Create cache for discovery
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
// Create InitializerMapper
|
||||
$initializerMapper = new InitializerMapper();
|
||||
|
||||
// Configure discovery to scan the tests/debug directory
|
||||
$debugPath = __DIR__;
|
||||
$config = new DiscoveryConfiguration(
|
||||
paths: [$debugPath],
|
||||
attributeMappers: [$initializerMapper],
|
||||
targetInterfaces: [],
|
||||
useCache: false
|
||||
);
|
||||
|
||||
// Create discovery service
|
||||
$discoveryService = new UnifiedDiscoveryService(
|
||||
pathProvider: $pathProvider,
|
||||
cache: $cache,
|
||||
clock: $clock,
|
||||
reflectionProvider: $reflectionProvider,
|
||||
configuration: $config,
|
||||
attributeMappers: [$initializerMapper],
|
||||
targetInterfaces: []
|
||||
);
|
||||
|
||||
echo "Scanning for debug initializers...\n";
|
||||
|
||||
$registry = $discoveryService->discover();
|
||||
|
||||
echo "Discovery completed!\n\n";
|
||||
|
||||
// Check if our debug initializer was found
|
||||
$initializerResults = $registry->attributes->get('App\\Framework\\DI\\Initializer');
|
||||
|
||||
echo "=== Debug Initializer Results ===\n";
|
||||
echo "Initializers found: " . count($initializerResults) . "\n\n";
|
||||
|
||||
if (! empty($initializerResults)) {
|
||||
foreach ($initializerResults as $mapping) {
|
||||
$className = $mapping->class->getFullyQualified();
|
||||
$methodName = $mapping->method ? $mapping->method->toString() : '(class-level)';
|
||||
echo "- {$className}::{$methodName}\n";
|
||||
|
||||
if ($mapping->mappedData) {
|
||||
echo " Return Type: " . ($mapping->mappedData['return'] ?? 'null') . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n=== Manual Execution Test ===\n";
|
||||
|
||||
// Now manually execute the debug initializer to see if it works
|
||||
$container->instance(\App\Framework\DI\Container::class, $container);
|
||||
|
||||
echo "Creating DebugInitializer...\n";
|
||||
$debugInitializer = new DebugInitializer($container);
|
||||
|
||||
echo "Executing void method...\n";
|
||||
$debugInitializer->debugTest();
|
||||
|
||||
echo "Executing service method...\n";
|
||||
$service = $debugInitializer->debugService();
|
||||
echo "Service created: " . json_encode($service) . "\n\n";
|
||||
|
||||
// Check if log file was created
|
||||
if (file_exists($logFile)) {
|
||||
echo "=== Execution Log ===\n";
|
||||
echo file_get_contents($logFile);
|
||||
} else {
|
||||
echo "❌ No execution log found\n";
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
66
tests/debug/test-dependency-graph-api.php
Normal file
66
tests/debug/test-dependency-graph-api.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\DI\InitializerDependencyGraph;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
|
||||
echo "=== Testing InitializerDependencyGraph API ===\n\n";
|
||||
|
||||
try {
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
$graph = new InitializerDependencyGraph($reflectionProvider);
|
||||
|
||||
// Add some test initializers
|
||||
$graph->addInitializer('App\\Cache\\Cache', 'App\\Config\\CacheInitializer', 'createCache');
|
||||
$graph->addInitializer('App\\Database\\Connection', 'App\\Config\\DatabaseInitializer', 'createConnection');
|
||||
$graph->addInitializer('App\\Logging\\Logger', 'App\\Config\\LoggerInitializer', 'createLogger');
|
||||
|
||||
echo "=== Testing New API Methods ===\n";
|
||||
|
||||
// Test hasNode
|
||||
echo "Has Cache node: " . ($graph->hasNode('App\\Cache\\Cache') ? 'Yes' : 'No') . "\n";
|
||||
echo "Has Redis node: " . ($graph->hasNode('App\\Cache\\Redis') ? 'Yes' : 'No') . "\n\n";
|
||||
|
||||
// Test getNode
|
||||
$cacheNode = $graph->getNode('App\\Cache\\Cache');
|
||||
if ($cacheNode) {
|
||||
echo "✅ Cache node found:\n";
|
||||
echo " " . $cacheNode->toString() . "\n";
|
||||
echo " Class: " . $cacheNode->getClassName() . "\n";
|
||||
echo " Method: " . $cacheNode->getMethodName() . "\n";
|
||||
echo " Dependencies: " . count($cacheNode->dependencies) . "\n\n";
|
||||
}
|
||||
|
||||
$missingNode = $graph->getNode('App\\NonExistent\\Service');
|
||||
echo "Missing node result: " . ($missingNode === null ? 'null (correct)' : 'found (unexpected)') . "\n\n";
|
||||
|
||||
// Test getNodes (new API)
|
||||
echo "=== Testing Node Enumeration ===\n";
|
||||
$nodes = $graph->getNodes();
|
||||
echo "Total nodes: " . count($nodes) . "\n";
|
||||
|
||||
foreach ($nodes as $returnType => $node) {
|
||||
echo "- $returnType: {$node->getClassName()}::{$node->getMethodName()}\n";
|
||||
}
|
||||
|
||||
echo "\n=== Testing Execution Order ===\n";
|
||||
$executionOrder = $graph->getExecutionOrder();
|
||||
echo "Execution order: " . implode(' → ', $executionOrder) . "\n\n";
|
||||
|
||||
// Test backward compatibility
|
||||
echo "=== Testing Backward Compatibility ===\n";
|
||||
$nodesArray = $graph->getNodesAsArray();
|
||||
echo "Nodes as array format (backward compatibility):\n";
|
||||
foreach ($nodesArray as $returnType => $nodeData) {
|
||||
echo "- $returnType: {$nodeData['class']}::{$nodeData['method']} (deps: " . count($nodeData['dependencies']) . ")\n";
|
||||
}
|
||||
|
||||
echo "\n✅ All InitializerDependencyGraph API tests passed!\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
63
tests/debug/test-dependency-graph-node.php
Normal file
63
tests/debug/test-dependency-graph-node.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Core\ValueObjects\ClassName;
|
||||
use App\Framework\Core\ValueObjects\MethodName;
|
||||
use App\Framework\DI\ValueObjects\DependencyGraphNode;
|
||||
|
||||
echo "=== Testing DependencyGraphNode Value Object ===\n\n";
|
||||
|
||||
try {
|
||||
// Test basic node creation
|
||||
$node = new DependencyGraphNode(
|
||||
returnType: 'App\\Service\\DatabaseService',
|
||||
className: ClassName::create('App\\Config\\DatabaseInitializer'),
|
||||
methodName: MethodName::create('createDatabaseService'),
|
||||
dependencies: ['App\\Database\\Connection', 'App\\Logging\\Logger']
|
||||
);
|
||||
|
||||
echo "✅ Node created successfully:\n";
|
||||
echo " " . $node->toString() . "\n\n";
|
||||
|
||||
// Test methods
|
||||
echo "=== Testing Node Methods ===\n";
|
||||
echo "Return Type: " . $node->returnType . "\n";
|
||||
echo "Class Name: " . $node->getClassName() . "\n";
|
||||
echo "Method Name: " . $node->getMethodName() . "\n";
|
||||
echo "Has Dependencies: " . ($node->hasDependencies() ? 'Yes' : 'No') . "\n";
|
||||
echo "Depends on Logger: " . ($node->dependsOn('App\\Logging\\Logger') ? 'Yes' : 'No') . "\n";
|
||||
echo "Depends on Redis: " . ($node->dependsOn('App\\Cache\\Redis') ? 'Yes' : 'No') . "\n\n";
|
||||
|
||||
// Test array conversion
|
||||
echo "=== Testing Array Conversion ===\n";
|
||||
$arrayData = $node->toArray();
|
||||
echo "Array format:\n";
|
||||
print_r($arrayData);
|
||||
|
||||
// Test from array creation
|
||||
echo "\n=== Testing From Array Creation ===\n";
|
||||
$nodeFromArray = DependencyGraphNode::fromArray('App\\Service\\DatabaseService', $arrayData);
|
||||
echo "Recreated from array: " . $nodeFromArray->toString() . "\n";
|
||||
echo "Equal to original: " . ($node->toString() === $nodeFromArray->toString() ? 'Yes' : 'No') . "\n\n";
|
||||
|
||||
// Test with additional dependencies
|
||||
echo "=== Testing Additional Dependencies ===\n";
|
||||
$nodeWithExtraDeps = $node->withDependencies(['App\\Cache\\Redis', 'App\\Events\\EventDispatcher']);
|
||||
echo "Original dependencies: " . count($node->dependencies) . "\n";
|
||||
echo "New dependencies: " . count($nodeWithExtraDeps->dependencies) . "\n";
|
||||
echo "New node toString: " . $nodeWithExtraDeps->toString() . "\n\n";
|
||||
|
||||
// Test debug info
|
||||
echo "=== Testing Debug Info ===\n";
|
||||
$debugInfo = $node->getDebugInfo();
|
||||
print_r($debugInfo);
|
||||
|
||||
echo "\n✅ All DependencyGraphNode tests passed!\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
145
tests/debug/test-discovery-cache.php
Normal file
145
tests/debug/test-discovery-cache.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\RedisCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Redis\RedisConfig;
|
||||
use App\Framework\Redis\RedisConnection;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Testing Discovery Cache with Redis ===\n\n";
|
||||
|
||||
try {
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$srcPath = $projectRoot . '/src';
|
||||
|
||||
// Create container and dependencies
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
$serializer = new JsonSerializer();
|
||||
|
||||
// Create Redis connection
|
||||
$redisConfig = new RedisConfig('redis', 6379, null, 1);
|
||||
$redisConnection = new RedisConnection($redisConfig, 'discovery');
|
||||
|
||||
// Try to connect
|
||||
echo "1. Testing Redis connection:\n";
|
||||
|
||||
try {
|
||||
// Test connection by getting Redis client
|
||||
$redis = $redisConnection->getClient();
|
||||
$redis->ping();
|
||||
echo " ✅ Redis connected successfully\n";
|
||||
} catch (Exception $e) {
|
||||
echo " ❌ Redis connection failed: " . $e->getMessage() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Create Redis cache
|
||||
$cacheDriver = new RedisCache($redisConnection);
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
echo "\n2. Creating Discovery Service with Redis cache:\n";
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
$discoveryService = $factory->createForDevelopment([$srcPath]);
|
||||
|
||||
echo " ✅ Discovery service created\n";
|
||||
|
||||
// Clear any existing cache
|
||||
echo "\n3. Clearing existing cache:\n";
|
||||
$redis = $redisConnection->getClient();
|
||||
$keys = $redis->keys('cache:discovery:*');
|
||||
if (! empty($keys)) {
|
||||
$redis->del($keys);
|
||||
echo " ✅ Cleared " . count($keys) . " existing cache keys\n";
|
||||
} else {
|
||||
echo " ✅ No existing cache keys found\n";
|
||||
}
|
||||
|
||||
// First discovery run - should cache
|
||||
echo "\n4. First discovery run (should miss cache):\n";
|
||||
$startTime = microtime(true);
|
||||
$registry1 = $discoveryService->discover();
|
||||
$firstRunTime = (microtime(true) - $startTime) * 1000;
|
||||
|
||||
echo " Items discovered: " . count($registry1) . "\n";
|
||||
echo " Time taken: " . number_format($firstRunTime, 2) . "ms\n";
|
||||
|
||||
// Check cache keys
|
||||
$cacheKeys = $redis->keys('cache:discovery:*');
|
||||
echo " Cache keys after first run: " . count($cacheKeys) . "\n";
|
||||
foreach ($cacheKeys as $key) {
|
||||
$ttl = $redis->ttl($key);
|
||||
$size = strlen($redis->get($key));
|
||||
echo " - $key (TTL: {$ttl}s, Size: " . number_format($size) . " bytes)\n";
|
||||
}
|
||||
|
||||
// Second discovery run - should hit cache
|
||||
echo "\n5. Second discovery run (should hit cache):\n";
|
||||
$startTime = microtime(true);
|
||||
$registry2 = $discoveryService->discover();
|
||||
$secondRunTime = (microtime(true) - $startTime) * 1000;
|
||||
|
||||
echo " Items discovered: " . count($registry2) . "\n";
|
||||
echo " Time taken: " . number_format($secondRunTime, 2) . "ms\n";
|
||||
|
||||
// Performance comparison
|
||||
$speedup = $firstRunTime / $secondRunTime;
|
||||
echo " Speedup: " . number_format($speedup, 2) . "x\n";
|
||||
|
||||
if ($speedup > 2) {
|
||||
echo " ✅ Cache is working effectively\n";
|
||||
} else {
|
||||
echo " ⚠️ Cache may not be working (speedup should be >2x)\n";
|
||||
}
|
||||
|
||||
// Verify identical results
|
||||
echo "\n6. Verifying cache integrity:\n";
|
||||
if (count($registry1) === count($registry2)) {
|
||||
echo " ✅ Same number of items returned\n";
|
||||
} else {
|
||||
echo " ❌ Different number of items: " . count($registry1) . " vs " . count($registry2) . "\n";
|
||||
}
|
||||
|
||||
// Test cache invalidation after file modification
|
||||
echo "\n7. Testing cache invalidation (simulated file change):\n";
|
||||
// We can't actually modify files, but we can check the cache mechanism
|
||||
|
||||
// Manual cache key inspection
|
||||
echo "\n8. Cache key analysis:\n";
|
||||
$cacheKeys = $redis->keys('cache:discovery:*');
|
||||
foreach ($cacheKeys as $key) {
|
||||
$data = $redis->get($key);
|
||||
$uncompressed = $data;
|
||||
if (str_starts_with($data, "\x1f\x8b")) { // gzip signature
|
||||
$uncompressed = gzuncompress($data);
|
||||
echo " Key: $key (compressed, original: " . number_format(strlen($data)) . " bytes, expanded: " . number_format(strlen($uncompressed)) . " bytes)\n";
|
||||
} else {
|
||||
echo " Key: $key (uncompressed, size: " . number_format(strlen($data)) . " bytes)\n";
|
||||
}
|
||||
|
||||
// Try to unserialize and check structure
|
||||
try {
|
||||
$unserialized = $serializer->deserialize($uncompressed);
|
||||
echo " - Unserialization: SUCCESS\n";
|
||||
echo " - Data type: " . gettype($unserialized) . "\n";
|
||||
if (is_object($unserialized)) {
|
||||
echo " - Object class: " . get_class($unserialized) . "\n";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo " - Unserialization: FAILED - " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
258
tests/debug/test-discovery-issue.php
Normal file
258
tests/debug/test-discovery-issue.php
Normal file
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\CacheInitializer;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\DiscoveryServiceBootstrapper;
|
||||
use App\Framework\Logging\DefaultLogger;
|
||||
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
|
||||
use App\Framework\Performance\PerformanceCategory;
|
||||
use App\Framework\Performance\PerformanceMetric;
|
||||
|
||||
// Simple mock performance collector for testing
|
||||
final class MockPerformanceCollector implements PerformanceCollectorInterface
|
||||
{
|
||||
public function startTiming(string $key, PerformanceCategory $category, array $context = []): void
|
||||
{
|
||||
}
|
||||
|
||||
public function endTiming(string $key): void
|
||||
{
|
||||
}
|
||||
|
||||
public function measure(string $key, PerformanceCategory $category, callable $callback, array $context = []): mixed
|
||||
{
|
||||
return $callback();
|
||||
}
|
||||
|
||||
public function recordMetric(string $key, PerformanceCategory $category, float $value, array $context = []): void
|
||||
{
|
||||
}
|
||||
|
||||
public function increment(string $key, PerformanceCategory $category, int $amount = 1, array $context = []): void
|
||||
{
|
||||
}
|
||||
|
||||
public function getMetrics(?PerformanceCategory $category = null): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getMetric(string $key): ?PerformanceMetric
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getTotalRequestTime(): float
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public function getTotalRequestMemory(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getPeakMemory(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setEnabled(bool $enabled): void
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
echo "=== Discovery Debug Test ===\n";
|
||||
|
||||
// Setup minimal container
|
||||
$container = new DefaultContainer();
|
||||
$basePath = realpath(__DIR__ . '/../..');
|
||||
|
||||
echo "Base path: $basePath\n";
|
||||
|
||||
// Add required services
|
||||
$container->instance(\App\Framework\Logging\Logger::class, new DefaultLogger());
|
||||
$container->instance(\App\Framework\Core\PathProvider::class, new PathProvider($basePath));
|
||||
$container->instance(\App\Framework\DateTime\Clock::class, new SystemClock());
|
||||
|
||||
// Create performance collector and cache using CacheInitializer
|
||||
$collector = new MockPerformanceCollector();
|
||||
$container->instance(\App\Framework\Performance\Contracts\PerformanceCollectorInterface::class, $collector);
|
||||
|
||||
$cacheInitializer = new CacheInitializer($collector, $container);
|
||||
$cache = $cacheInitializer();
|
||||
$container->instance(\App\Framework\Cache\Cache::class, $cache);
|
||||
|
||||
// Force development environment for testing
|
||||
if (! $container->has(\App\Framework\Config\AppConfig::class)) {
|
||||
// Create a mock AppConfig for development
|
||||
$appConfig = new class () {
|
||||
public string $environment = 'development';
|
||||
};
|
||||
$container->instance(\App\Framework\Config\AppConfig::class, $appConfig);
|
||||
}
|
||||
|
||||
// Create bootstrapper
|
||||
$clock = $container->get(\App\Framework\DateTime\Clock::class);
|
||||
$bootstrapper = new DiscoveryServiceBootstrapper($container, $clock);
|
||||
|
||||
echo "Starting discovery...\n";
|
||||
|
||||
// Clear cache first to force fresh discovery
|
||||
$cache = $container->get(\App\Framework\Cache\Cache::class);
|
||||
echo "Clearing all cache...\n";
|
||||
|
||||
// Try to clear all cache
|
||||
try {
|
||||
$reflection = new \ReflectionClass($cache);
|
||||
if ($reflection->hasMethod('clear')) {
|
||||
$cache->clear();
|
||||
echo "Cache cleared successfully.\n";
|
||||
} else {
|
||||
echo "Cache doesn't have clear method.\n";
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
echo "Cache clear failed: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
// Enable debug mode to see what's happening
|
||||
ini_set('error_reporting', E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
|
||||
// Try bootstrap
|
||||
$results = $bootstrapper->bootstrap();
|
||||
|
||||
echo "Discovery completed successfully!\n";
|
||||
|
||||
// Get the discovery service to check configuration
|
||||
$discoveryService = $container->get(\App\Framework\Discovery\UnifiedDiscoveryService::class);
|
||||
|
||||
// Get configuration from the service
|
||||
// Since it's readonly, we can't access the config directly, so let's test the paths
|
||||
echo "Testing if files exist in expected paths:\n";
|
||||
$basePath = $container->get(\App\Framework\Core\PathProvider::class)->getBasePath();
|
||||
$srcPath = $basePath . '/src';
|
||||
$appPath = $basePath . '/app';
|
||||
|
||||
echo "Base path: $basePath\n";
|
||||
echo "Src path exists: " . (is_dir($srcPath) ? 'YES' : 'NO') . "\n";
|
||||
echo "App path exists: " . (is_dir($appPath) ? 'YES' : 'NO') . "\n";
|
||||
|
||||
if (is_dir($srcPath)) {
|
||||
// Use recursive directory iterator instead of glob with **
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($srcPath, \RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
$phpFiles = [];
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->getExtension() === 'php') {
|
||||
$phpFiles[] = $file->getRealPath();
|
||||
}
|
||||
}
|
||||
echo "PHP files in src/: " . count($phpFiles) . "\n";
|
||||
|
||||
// Check for specific ConsoleCommand files
|
||||
$commandFiles = [];
|
||||
foreach ($phpFiles as $file) {
|
||||
if (strpos(file_get_contents($file), 'ConsoleCommand') !== false) {
|
||||
$commandFiles[] = $file;
|
||||
}
|
||||
}
|
||||
echo "Files with ConsoleCommand: " . count($commandFiles) . "\n";
|
||||
foreach (array_slice($commandFiles, 0, 3) as $file) {
|
||||
echo " - " . str_replace($basePath, '', $file) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Check what was discovered
|
||||
echo "\nDiscovery results:\n";
|
||||
echo "Attributes found: " . $results->attributes->count() . "\n";
|
||||
echo "Routes found: " . $results->routes->count() . "\n";
|
||||
echo "Interfaces found: " . $results->interfaces->count() . "\n";
|
||||
|
||||
// Show attribute types
|
||||
echo "Attribute types found: " . implode(', ', $results->attributes->getAllTypes()) . "\n";
|
||||
|
||||
// Check for console commands specifically
|
||||
$commandAttributes = $results->attributes->get('App\\Framework\\Console\\ConsoleCommand');
|
||||
echo "Console commands found: " . count($commandAttributes) . "\n";
|
||||
|
||||
// Test: Manually check one specific file to see if it would be processed
|
||||
echo "\n=== Manual File Processing Test ===\n";
|
||||
$testFile = $basePath . '/src/Framework/Discovery/Commands/ClearDiscoveryCache.php';
|
||||
if (file_exists($testFile)) {
|
||||
echo "Test file exists: $testFile\n";
|
||||
|
||||
// Check if it contains ConsoleCommand attribute
|
||||
$content = file_get_contents($testFile);
|
||||
if (strpos($content, '#[ConsoleCommand') !== false) {
|
||||
echo "File contains #[ConsoleCommand attribute\n";
|
||||
} else {
|
||||
echo "File does NOT contain #[ConsoleCommand attribute\n";
|
||||
}
|
||||
|
||||
// Try to analyze the file structure
|
||||
$tokens = token_get_all($content);
|
||||
$classes = [];
|
||||
$namespace = '';
|
||||
|
||||
for ($i = 0; $i < count($tokens); $i++) {
|
||||
if (is_array($tokens[$i])) {
|
||||
if ($tokens[$i][0] === T_NAMESPACE) {
|
||||
// Get namespace
|
||||
$j = $i + 1;
|
||||
while ($j < count($tokens) && $tokens[$j] !== ';') {
|
||||
if (is_array($tokens[$j]) && $tokens[$j][0] === T_NAME_QUALIFIED) {
|
||||
$namespace = $tokens[$j][1];
|
||||
}
|
||||
$j++;
|
||||
}
|
||||
} elseif ($tokens[$i][0] === T_CLASS) {
|
||||
// Get class name
|
||||
$j = $i + 1;
|
||||
while ($j < count($tokens)) {
|
||||
if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {
|
||||
$classes[] = $namespace . '\\' . $tokens[$j][1];
|
||||
|
||||
break;
|
||||
}
|
||||
$j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Classes found in file: " . implode(', ', $classes) . "\n";
|
||||
} else {
|
||||
echo "Test file does not exist: $testFile\n";
|
||||
}
|
||||
|
||||
// List some commands
|
||||
foreach (array_slice($commandAttributes, 0, 5) as $commandMapping) {
|
||||
$commandData = $commandMapping->mappedData;
|
||||
$commandName = is_array($commandData) && isset($commandData['name'])
|
||||
? $commandData['name']
|
||||
: 'Unknown';
|
||||
echo " - $commandName (class: {$commandMapping->class->getFullyQualified()})\n";
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
echo "ERROR: " . $e->getMessage() . "\n";
|
||||
echo "Trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
50
tests/debug/test-discovery-minimal.php
Normal file
50
tests/debug/test-discovery-minimal.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
|
||||
try {
|
||||
echo "=== Minimal Discovery Test ===\n";
|
||||
|
||||
// Create minimal container
|
||||
$container = new DefaultContainer();
|
||||
$basePath = realpath(__DIR__ . '/../..');
|
||||
$pathProvider = new PathProvider($basePath);
|
||||
$clock = new SystemClock();
|
||||
$cache = new GeneralCache(); // Use general cache for simplicity
|
||||
|
||||
echo "Base path: $basePath\n";
|
||||
|
||||
// Create factory
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
|
||||
// Create discovery service with explicit paths
|
||||
$discoveryService = $factory->createForDevelopment([$basePath . '/src']);
|
||||
|
||||
echo "Created DiscoveryService for development\n";
|
||||
|
||||
// Test discovery directly
|
||||
echo "Starting discovery...\n";
|
||||
$results = $discoveryService->discover();
|
||||
|
||||
echo "Discovery completed!\n";
|
||||
echo "Attributes found: " . $results->attributes->count() . "\n";
|
||||
echo "Routes found: " . $results->routes->count() . "\n";
|
||||
echo "Interfaces found: " . $results->interfaces->count() . "\n";
|
||||
|
||||
if ($results->attributes->count() > 0) {
|
||||
echo "Attribute types: " . implode(', ', $results->attributes->getAllTypes()) . "\n";
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
echo "ERROR: " . $e->getMessage() . "\n";
|
||||
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
echo "Trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
79
tests/debug/test-discovery-pipeline.php
Normal file
79
tests/debug/test-discovery-pipeline.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Discovery\Processing\ClassExtractor;
|
||||
use App\Framework\Discovery\Processing\ProcessingContext;
|
||||
use App\Framework\Discovery\ValueObjects\FileContext;
|
||||
use App\Framework\Filesystem\File;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
|
||||
try {
|
||||
echo "=== Discovery Pipeline Debug Test ===\n";
|
||||
|
||||
$testFile = '/var/www/html/src/Framework/Discovery/Commands/ClearDiscoveryCache.php';
|
||||
echo "Testing file: $testFile\n";
|
||||
|
||||
// Create file system service
|
||||
$fileSystemService = new FileSystemService();
|
||||
|
||||
// Create file object using static factory method
|
||||
$fileInfo = new \SplFileInfo($testFile);
|
||||
$file = File::fromSplFileInfo($fileInfo);
|
||||
echo "File created: " . $file->getPath() . "\n";
|
||||
|
||||
// Test ClassExtractor
|
||||
$classExtractor = new ClassExtractor($fileSystemService);
|
||||
$classNames = $classExtractor->extractFromFile($file);
|
||||
|
||||
echo "Classes extracted: " . count($classNames) . "\n";
|
||||
foreach ($classNames as $className) {
|
||||
echo " - " . $className->getFullyQualified() . "\n";
|
||||
}
|
||||
|
||||
// Test FileContext creation (like FileStreamProcessor does)
|
||||
$fileContext = FileContext::fromFile($file)->withClassNames($classNames);
|
||||
|
||||
// Test ProcessingContext
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
$processingContext = new ProcessingContext($reflectionProvider);
|
||||
|
||||
echo "FileContext created\n";
|
||||
echo "FileContext class names: " . count($fileContext->getClassNames()) . "\n";
|
||||
foreach ($fileContext->getClassNames() as $className) {
|
||||
echo " - " . $className->getFullyQualified() . "\n";
|
||||
|
||||
// Test reflection
|
||||
$reflection = $processingContext->getReflection($className);
|
||||
if ($reflection === null) {
|
||||
echo " ERROR: No reflection found for class\n";
|
||||
} else {
|
||||
echo " Reflection OK: " . $reflection->getName() . "\n";
|
||||
|
||||
// Check methods
|
||||
$methods = $reflection->getMethods();
|
||||
echo " Methods: " . count($methods) . "\n";
|
||||
|
||||
foreach ($methods as $method) {
|
||||
$attributes = $method->getAttributes();
|
||||
if (count($attributes) > 0) {
|
||||
echo " Method {$method->getName()} has " . count($attributes) . " attributes:\n";
|
||||
foreach ($attributes as $attr) {
|
||||
echo " - " . $attr->getName() . "\n";
|
||||
if ($attr->getName() === 'App\\Framework\\Console\\ConsoleCommand') {
|
||||
echo " FOUND CONSOLE COMMAND ATTRIBUTE!\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
echo "ERROR: " . $e->getMessage() . "\n";
|
||||
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
echo "Trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
289
tests/debug/test-discovery-refactor.php
Normal file
289
tests/debug/test-discovery-refactor.php
Normal file
@@ -0,0 +1,289 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Test script for the refactored Discovery module
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\Discovery\UnifiedDiscoveryService;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryOptions;
|
||||
use App\Framework\Discovery\ValueObjects\ScanType;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Logging\DefaultLogger;
|
||||
use App\Framework\Logging\Handlers\ConsoleHandler;
|
||||
use App\Framework\Logging\LogLevel;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
|
||||
echo "🔍 Testing Refactored Discovery Module\n";
|
||||
echo "=====================================\n";
|
||||
|
||||
try {
|
||||
// Setup dependencies
|
||||
$pathProvider = new PathProvider(__DIR__ . '/../..');
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new PhpSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
$clock = new SystemClock();
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
$fileSystemService = new FileSystemService();
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
|
||||
// Setup logger
|
||||
$consoleHandler = new ConsoleHandler();
|
||||
$logger = new DefaultLogger(LogLevel::INFO, [$consoleHandler]);
|
||||
|
||||
echo "✅ Dependencies initialized\n";
|
||||
|
||||
// Create discovery service with new architecture
|
||||
$discoveryService = new UnifiedDiscoveryService(
|
||||
pathProvider: $pathProvider,
|
||||
cache: $cache,
|
||||
clock: $clock,
|
||||
reflectionProvider: $reflectionProvider,
|
||||
attributeMappers: [], // Add specific mappers if needed
|
||||
targetInterfaces: [], // Add specific interfaces if needed
|
||||
useCache: false, // Disable cache for testing
|
||||
contextSuffix: '_test',
|
||||
logger: $logger,
|
||||
eventDispatcher: null,
|
||||
memoryMonitor: $memoryMonitor,
|
||||
fileSystemService: $fileSystemService
|
||||
);
|
||||
|
||||
// Test single directory to debug
|
||||
echo "\n🔍 Debug: Let's first check if files are being processed properly\n";
|
||||
|
||||
// Test if the Admin directory has PHP files
|
||||
$adminDir = $pathProvider->getBasePath() . '/src/Application/Admin';
|
||||
echo "Testing directory: $adminDir\n";
|
||||
|
||||
if (is_dir($adminDir)) {
|
||||
$phpFiles = glob($adminDir . '/*.php');
|
||||
echo "PHP files found: " . count($phpFiles) . "\n";
|
||||
if (count($phpFiles) > 0) {
|
||||
echo "First few files:\n";
|
||||
foreach (array_slice($phpFiles, 0, 3) as $file) {
|
||||
echo " - " . basename($file) . "\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "Directory not found!\n";
|
||||
}
|
||||
|
||||
echo "✅ Discovery service created with new architecture\n";
|
||||
|
||||
// Quick manual test of class extraction
|
||||
echo "\n🔍 Manual class extraction test:\n";
|
||||
$testFile = $adminDir . '/Dashboard.php';
|
||||
if (file_exists($testFile)) {
|
||||
echo "Testing class extraction on: " . basename($testFile) . "\n";
|
||||
|
||||
// Create a file object
|
||||
$fileInfo = new \SplFileInfo($testFile);
|
||||
$file = \App\Framework\Filesystem\File::fromSplFileInfo($fileInfo);
|
||||
|
||||
// Test class extraction
|
||||
$extractor = new \App\Framework\Discovery\Processing\ClassExtractor($fileSystemService);
|
||||
$classNames = $extractor->extractFromFile($file);
|
||||
|
||||
echo "Classes found: " . count($classNames) . "\n";
|
||||
foreach ($classNames as $className) {
|
||||
echo " - " . $className->getFullyQualified() . "\n";
|
||||
}
|
||||
|
||||
// Debug: Let's read the file content directly and check patterns
|
||||
echo "\n🔍 Debug file content:\n";
|
||||
$content = $fileSystemService->readFile($file);
|
||||
echo "File size: " . strlen($content) . " characters\n";
|
||||
|
||||
// Test if content has class keyword
|
||||
if (str_contains($content, 'class ')) {
|
||||
echo "✅ Contains 'class ' keyword\n";
|
||||
} else {
|
||||
echo "❌ Does NOT contain 'class ' keyword\n";
|
||||
}
|
||||
|
||||
// Test namespace extraction
|
||||
if (preg_match('/^\s*namespace\s+([^;]+);/m', $content, $matches)) {
|
||||
echo "✅ Namespace found: " . $matches[1] . "\n";
|
||||
} else {
|
||||
echo "❌ No namespace found\n";
|
||||
}
|
||||
|
||||
// Test class extraction pattern
|
||||
$pattern = '/^\s*(?:final\s+)?(?:abstract\s+)?(?:readonly\s+)?class\s+([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)/mi';
|
||||
if (preg_match_all($pattern, $content, $matches)) {
|
||||
echo "✅ Class pattern matched: " . count($matches[1]) . " classes\n";
|
||||
foreach ($matches[1] as $className) {
|
||||
echo " - Class: $className\n";
|
||||
}
|
||||
} else {
|
||||
echo "❌ Class pattern did not match\n";
|
||||
}
|
||||
|
||||
// Test reflection on the class directly
|
||||
echo "\n🔍 Manual reflection test on Dashboard class:\n";
|
||||
|
||||
try {
|
||||
$dashboardClass = \App\Application\Admin\Dashboard::class;
|
||||
$reflection = new \ReflectionClass($dashboardClass);
|
||||
echo "✅ Reflection created for: $dashboardClass\n";
|
||||
|
||||
$methods = $reflection->getMethods();
|
||||
echo "Methods found: " . count($methods) . "\n";
|
||||
|
||||
foreach ($methods as $method) {
|
||||
$attributes = $method->getAttributes();
|
||||
if (! empty($attributes)) {
|
||||
echo " Method {$method->getName()} has " . count($attributes) . " attributes:\n";
|
||||
foreach ($attributes as $attr) {
|
||||
echo " - " . $attr->getName() . "\n";
|
||||
if (str_contains($attr->getName(), 'Route')) {
|
||||
echo " ✅ ROUTE ATTRIBUTE FOUND!\n";
|
||||
|
||||
try {
|
||||
$instance = $attr->newInstance();
|
||||
echo " - Instance created successfully\n";
|
||||
if (method_exists($instance, 'path')) {
|
||||
echo " - Path: " . $instance->path . "\n";
|
||||
}
|
||||
if (method_exists($instance, 'method')) {
|
||||
echo " - Method: " . $instance->method->value . "\n";
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo " - Failed to create instance: " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "❌ Reflection failed: " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Test health status
|
||||
echo "\n📊 Health Status:\n";
|
||||
$health = $discoveryService->getHealthStatus();
|
||||
foreach ($health as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
echo " $key:\n";
|
||||
foreach ($value as $subKey => $subValue) {
|
||||
echo " $subKey: " . (is_array($subValue) ? json_encode($subValue) : $subValue) . "\n";
|
||||
}
|
||||
} else {
|
||||
echo " $key: $value\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Test discovery with limited scope for faster testing
|
||||
echo "\n🔍 Running Discovery (limited scope)...\n";
|
||||
$startTime = microtime(true);
|
||||
$startMemory = memory_get_usage(true);
|
||||
|
||||
$options = new DiscoveryOptions(
|
||||
scanType: ScanType::FULL,
|
||||
paths: [
|
||||
$pathProvider->getBasePath() . '/src/Application/Admin',
|
||||
],
|
||||
useCache: false
|
||||
);
|
||||
|
||||
$registry = $discoveryService->discoverWithOptions($options);
|
||||
|
||||
$endTime = microtime(true);
|
||||
$endMemory = memory_get_usage(true);
|
||||
|
||||
echo "✅ Discovery completed!\n";
|
||||
echo "\n📈 Results:\n";
|
||||
echo " Total items: " . count($registry) . "\n";
|
||||
echo " Attributes: " . count($registry->attributes) . "\n";
|
||||
echo " Interfaces: " . count($registry->interfaces) . "\n";
|
||||
echo " Routes: " . count($registry->routes) . "\n";
|
||||
echo " Templates: " . count($registry->templates) . "\n";
|
||||
|
||||
echo "\n⏱️ Performance:\n";
|
||||
echo " Duration: " . round(($endTime - $startTime) * 1000, 2) . "ms\n";
|
||||
echo " Memory used: " . round(($endMemory - $startMemory) / 1024 / 1024, 2) . "MB\n";
|
||||
echo " Peak memory: " . round(memory_get_peak_usage(true) / 1024 / 1024, 2) . "MB\n";
|
||||
|
||||
// Test registry statistics
|
||||
echo "\n📊 Memory Statistics:\n";
|
||||
$stats = $registry->getMemoryStats();
|
||||
foreach ($stats as $type => $stat) {
|
||||
if (is_array($stat)) {
|
||||
echo " $type:\n";
|
||||
foreach ($stat as $key => $value) {
|
||||
echo " $key: $value\n";
|
||||
}
|
||||
} else {
|
||||
echo " $type: $stat\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Show some example discovered items
|
||||
echo "\n🔍 Sample Discoveries:\n";
|
||||
|
||||
// Show attributes
|
||||
$attributeTypes = $registry->attributes->getAllTypes();
|
||||
if (! empty($attributeTypes)) {
|
||||
echo " Attribute Types Found:\n";
|
||||
foreach (array_slice($attributeTypes, 0, 5) as $type) {
|
||||
$count = count($registry->attributes->get($type));
|
||||
echo " - $type ($count instances)\n";
|
||||
}
|
||||
|
||||
// Debug: Look for Route attributes specifically
|
||||
echo "\n 🔍 Debug - Looking for Route attributes:\n";
|
||||
foreach ($attributeTypes as $type) {
|
||||
if (str_contains($type, 'Route')) {
|
||||
echo " ✅ Route attribute found: $type\n";
|
||||
$instances = $registry->attributes->get($type);
|
||||
foreach ($instances as $instance) {
|
||||
echo " - Method: " . ($instance->method ? $instance->method->toString() : 'class-level') . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! array_filter($attributeTypes, fn ($type) => str_contains($type, 'Route'))) {
|
||||
echo " ❌ No Route attributes found. Looking for all attribute types:\n";
|
||||
foreach ($attributeTypes as $type) {
|
||||
echo " - $type\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show routes
|
||||
$routes = $registry->routes->getAll();
|
||||
if (! empty($routes)) {
|
||||
echo " Routes Found:\n";
|
||||
foreach (array_slice($routes, 0, 3) as $route) {
|
||||
echo " - {$route->method->value} {$route->path} -> {$route->class->getShortName()}::{$route->handler->toString()}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n✅ Discovery refactoring test completed successfully!\n";
|
||||
echo "\n🎯 Key Improvements:\n";
|
||||
echo " ✅ Separated concerns into focused components\n";
|
||||
echo " ✅ Shared reflection context (no duplication)\n";
|
||||
echo " ✅ Stream-based processing for memory efficiency\n";
|
||||
echo " ✅ Modern value object architecture\n";
|
||||
echo " ✅ Improved caching strategy\n";
|
||||
echo " ✅ Better error handling and logging\n";
|
||||
|
||||
} catch (Throwable $e) {
|
||||
echo "\n❌ Error during discovery test:\n";
|
||||
echo " Message: " . $e->getMessage() . "\n";
|
||||
echo " File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
echo " Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
116
tests/debug/test-discovery-registry-completeness.php
Normal file
116
tests/debug/test-discovery-registry-completeness.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\DI\Initializer;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Testing DiscoveryRegistry Completeness ===\n\n";
|
||||
|
||||
try {
|
||||
// Setup
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$srcPath = $projectRoot . '/src';
|
||||
|
||||
echo "Project root: $projectRoot\n";
|
||||
echo "Source path: $srcPath\n\n";
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
|
||||
// Create cache for discovery
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
// Use DiscoveryServiceFactory like in production
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
$discoveryService = $factory->createForDevelopment([$srcPath]);
|
||||
|
||||
echo "Starting full discovery scan using DiscoveryServiceFactory...\n";
|
||||
$registry = $discoveryService->discover();
|
||||
echo "Discovery completed!\n\n";
|
||||
|
||||
// Analyze registry completeness
|
||||
echo "=== Registry Analysis ===\n";
|
||||
echo "Total items in registry: " . count($registry) . "\n";
|
||||
echo "Is empty: " . ($registry->isEmpty() ? 'Yes' : 'No') . "\n\n";
|
||||
|
||||
// Check individual registries
|
||||
echo "=== Individual Registry Stats ===\n";
|
||||
echo "Attributes: " . count($registry->attributes) . " items\n";
|
||||
echo "Interfaces: " . count($registry->interfaces) . " items\n";
|
||||
echo "Routes: " . count($registry->routes) . " items\n";
|
||||
echo "Templates: " . count($registry->templates) . " items\n\n";
|
||||
|
||||
// Check specific attribute types
|
||||
echo "=== Attribute Type Analysis ===\n";
|
||||
$attributeTypes = $registry->attributes->getAllTypes();
|
||||
echo "Total attribute types found: " . count($attributeTypes) . "\n\n";
|
||||
|
||||
foreach ($attributeTypes as $type) {
|
||||
$count = $registry->attributes->getCount($type);
|
||||
$shortType = substr(strrchr($type, '\\'), 1) ?: $type;
|
||||
echo "- $shortType: $count items\n";
|
||||
}
|
||||
|
||||
// Focus on Initializer specifically
|
||||
echo "\n=== Initializer Analysis ===\n";
|
||||
$initializerClass = Initializer::class;
|
||||
$initializerMappings = $registry->attributes->get($initializerClass);
|
||||
|
||||
echo "Initializer attribute class: $initializerClass\n";
|
||||
echo "Found initializers: " . count($initializerMappings) . "\n\n";
|
||||
|
||||
if (empty($initializerMappings)) {
|
||||
echo "❌ No initializers found! Checking alternative lookups...\n";
|
||||
|
||||
// Try different class name formats
|
||||
$alternativeNames = [
|
||||
'App\\Framework\\DI\\Initializer',
|
||||
'App\\\\Framework\\\\DI\\\\Initializer',
|
||||
'\\App\\Framework\\DI\\Initializer',
|
||||
];
|
||||
|
||||
foreach ($alternativeNames as $altName) {
|
||||
$altMappings = $registry->attributes->get($altName);
|
||||
echo "- $altName: " . count($altMappings) . " items\n";
|
||||
}
|
||||
|
||||
echo "\nAll available attribute types:\n";
|
||||
foreach ($attributeTypes as $type) {
|
||||
echo "- $type\n";
|
||||
}
|
||||
} else {
|
||||
echo "✅ Initializers found! Showing first 5:\n";
|
||||
for ($i = 0; $i < min(5, count($initializerMappings)); $i++) {
|
||||
$mapping = $initializerMappings[$i];
|
||||
$className = $mapping->class->getShortName();
|
||||
$methodName = $mapping->method ? $mapping->method->toString() : '(class-level)';
|
||||
echo "- $className::$methodName\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Memory stats
|
||||
echo "\n=== Memory Statistics ===\n";
|
||||
$memStats = $registry->getMemoryStats();
|
||||
echo "Total estimated memory: " . number_format($memStats['total_estimated_bytes']) . " bytes\n";
|
||||
echo "Attributes memory: " . $memStats['attributes']['memory_footprint'] . "\n";
|
||||
|
||||
// Check if registry got optimized
|
||||
$registry->optimize();
|
||||
echo "\n✅ Registry analysis completed!\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
147
tests/debug/test-discovery-service-internals.php
Normal file
147
tests/debug/test-discovery-service-internals.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\DI\InitializerMapper;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Discovery\UnifiedDiscoveryService;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Comparing UnifiedDiscoveryService Internals ===\n\n";
|
||||
|
||||
try {
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$srcPath = $projectRoot . '/src';
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
// Factory-based service
|
||||
echo "=== Factory-Based Service ===\n";
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
$factoryService = $factory->createForDevelopment([$srcPath]);
|
||||
|
||||
// Direct service
|
||||
echo "=== Direct Service ===\n";
|
||||
$fileSystemService = new FileSystemService();
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
|
||||
$directConfig = new DiscoveryConfiguration(
|
||||
paths: [$srcPath],
|
||||
attributeMappers: [new InitializerMapper()],
|
||||
targetInterfaces: [],
|
||||
useCache: false
|
||||
);
|
||||
|
||||
$directService = new UnifiedDiscoveryService(
|
||||
pathProvider: $pathProvider,
|
||||
cache: $cache,
|
||||
clock: $clock,
|
||||
reflectionProvider: $reflectionProvider,
|
||||
configuration: $directConfig,
|
||||
attributeMappers: [new InitializerMapper()],
|
||||
targetInterfaces: []
|
||||
);
|
||||
|
||||
// Compare configurations using reflection
|
||||
$factoryReflection = new ReflectionClass($factoryService);
|
||||
$directReflection = new ReflectionClass($directService);
|
||||
|
||||
echo "=== Configuration Comparison ===\n";
|
||||
|
||||
// Configuration
|
||||
$factoryConfigProp = $factoryReflection->getProperty('configuration');
|
||||
$factoryConfigProp->setAccessible(true);
|
||||
$factoryConfig = $factoryConfigProp->getValue($factoryService);
|
||||
|
||||
$directConfigProp = $directReflection->getProperty('configuration');
|
||||
$directConfigProp->setAccessible(true);
|
||||
$directConfigPropValue = $directConfigProp->getValue($directService);
|
||||
|
||||
echo "Factory Config:\n";
|
||||
print_r($factoryConfig->toArray());
|
||||
echo "\nDirect Config:\n";
|
||||
print_r($directConfigPropValue->toArray());
|
||||
|
||||
// Compare attribute mappers
|
||||
echo "\n=== Attribute Mappers Comparison ===\n";
|
||||
|
||||
$factoryMappersProp = $factoryReflection->getProperty('attributeMappers');
|
||||
$factoryMappersProp->setAccessible(true);
|
||||
$factoryMappers = $factoryMappersProp->getValue($factoryService);
|
||||
|
||||
$directMappersProp = $directReflection->getProperty('attributeMappers');
|
||||
$directMappersProp->setAccessible(true);
|
||||
$directMappers = $directMappersProp->getValue($directService);
|
||||
|
||||
echo "Factory Mappers (" . count($factoryMappers) . "):\n";
|
||||
foreach ($factoryMappers as $i => $mapper) {
|
||||
echo " $i: " . get_class($mapper) . "\n";
|
||||
}
|
||||
|
||||
echo "\nDirect Mappers (" . count($directMappers) . "):\n";
|
||||
foreach ($directMappers as $i => $mapper) {
|
||||
echo " $i: " . get_class($mapper) . "\n";
|
||||
}
|
||||
|
||||
// Compare paths
|
||||
echo "\n=== Paths Comparison ===\n";
|
||||
echo "Factory paths: " . json_encode($factoryConfig->paths) . "\n";
|
||||
echo "Direct paths: " . json_encode($directConfigPropValue->paths) . "\n";
|
||||
|
||||
// Check other possible differences
|
||||
echo "\n=== Other Properties ===\n";
|
||||
|
||||
$factoryFileSystemProp = $factoryReflection->getProperty('fileSystemService');
|
||||
$factoryFileSystemProp->setAccessible(true);
|
||||
$factoryFileSystem = $factoryFileSystemProp->getValue($factoryService);
|
||||
|
||||
$directFileSystemProp = $directReflection->getProperty('fileSystemService');
|
||||
$directFileSystemProp->setAccessible(true);
|
||||
$directFileSystem = $directFileSystemProp->getValue($directService);
|
||||
|
||||
echo "Factory FileSystemService: " . ($factoryFileSystem ? get_class($factoryFileSystem) : 'null') . "\n";
|
||||
echo "Direct FileSystemService: " . ($directFileSystem ? get_class($directFileSystem) : 'null') . "\n";
|
||||
|
||||
$factoryReflectionProp = $factoryReflection->getProperty('reflectionProvider');
|
||||
$factoryReflectionProp->setAccessible(true);
|
||||
$factoryReflectionProvider = $factoryReflectionProp->getValue($factoryService);
|
||||
|
||||
$directReflectionProp = $directReflection->getProperty('reflectionProvider');
|
||||
$directReflectionProp->setAccessible(true);
|
||||
$directReflectionProvider = $directReflectionProp->getValue($directService);
|
||||
|
||||
echo "Factory ReflectionProvider: " . get_class($factoryReflectionProvider) . "\n";
|
||||
echo "Direct ReflectionProvider: " . get_class($directReflectionProvider) . "\n";
|
||||
|
||||
// Now run both discoveries and compare the exact process
|
||||
echo "\n=== Discovery Process Comparison ===\n";
|
||||
|
||||
echo "Running factory discovery...\n";
|
||||
$factoryRegistry = $factoryService->discover();
|
||||
echo "Factory result: " . count($factoryRegistry) . " total, " .
|
||||
$factoryRegistry->attributes->getCount('App\\Framework\\DI\\Initializer') . " initializers\n";
|
||||
|
||||
echo "Running direct discovery...\n";
|
||||
$directRegistry = $directService->discover();
|
||||
echo "Direct result: " . count($directRegistry) . " total, " .
|
||||
$directRegistry->attributes->getCount('App\\Framework\\DI\\Initializer') . " initializers\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
283
tests/debug/test-event-system.php
Normal file
283
tests/debug/test-event-system.php
Normal file
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Test script for the Discovery Event System
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\Events\EventDispatcher;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Events\EventAggregator;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryOptions;
|
||||
use App\Framework\Discovery\ValueObjects\ScanType;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Logging\DefaultLogger;
|
||||
use App\Framework\Logging\Handlers\ConsoleHandler;
|
||||
use App\Framework\Logging\LogLevel;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
|
||||
echo "🎯 Testing Discovery Event System\n";
|
||||
echo "================================\n";
|
||||
|
||||
try {
|
||||
// Setup basic dependencies
|
||||
$pathProvider = new PathProvider(__DIR__ . '/../..');
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new PhpSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
$clock = new SystemClock();
|
||||
$container = new DefaultContainer();
|
||||
|
||||
// Create event dispatcher
|
||||
$eventDispatcher = new EventDispatcher();
|
||||
|
||||
// Optional dependencies for testing
|
||||
$consoleHandler = new ConsoleHandler();
|
||||
$logger = new DefaultLogger(LogLevel::INFO, [$consoleHandler]);
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
$fileSystemService = new FileSystemService();
|
||||
|
||||
// Register optional services in container
|
||||
$container->singleton(\App\Framework\Logging\Logger::class, $logger);
|
||||
$container->singleton(\App\Framework\Performance\MemoryMonitor::class, $memoryMonitor);
|
||||
$container->singleton(\App\Framework\Reflection\ReflectionProvider::class, $reflectionProvider);
|
||||
$container->singleton(\App\Framework\Filesystem\FileSystemService::class, $fileSystemService);
|
||||
$container->singleton(\App\Framework\Core\Events\EventDispatcher::class, $eventDispatcher);
|
||||
|
||||
echo "✅ Dependencies initialized\n";
|
||||
|
||||
// Test 1: Event Aggregator Setup
|
||||
echo "\n🧪 Test 1: Event Aggregator Setup\n";
|
||||
|
||||
$eventAggregator = new EventAggregator($eventDispatcher, $clock, $logger);
|
||||
echo "✅ Event Aggregator created and handlers registered\n";
|
||||
|
||||
$initialStats = $eventAggregator->getStatistics();
|
||||
echo " Initial Statistics: " . json_encode($initialStats) . "\n";
|
||||
|
||||
// Test 2: Discovery Service with Event Integration
|
||||
echo "\n🧪 Test 2: Discovery Service with Event Integration\n";
|
||||
|
||||
// Create factory
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
|
||||
// Create configuration with aggressive memory constraints to trigger events
|
||||
$config = new DiscoveryConfiguration(
|
||||
paths: [$pathProvider->getBasePath() . '/src/Application/Admin'],
|
||||
useCache: false,
|
||||
enableEventDispatcher: true,
|
||||
enableMemoryMonitoring: true,
|
||||
enablePerformanceTracking: true,
|
||||
memoryLimitMB: 16, // Very low limit to trigger memory events
|
||||
maxFilesPerBatch: 5, // Small batches to trigger chunk events
|
||||
memoryPressureThreshold: 0.5 // Lower threshold for more events
|
||||
);
|
||||
|
||||
// Create service with full event integration
|
||||
$discoveryService = $factory->create($config);
|
||||
echo "✅ Discovery service created with event integration\n";
|
||||
|
||||
// Test 3: Run Discovery to Generate Events
|
||||
echo "\n🧪 Test 3: Running Discovery to Generate Events\n";
|
||||
|
||||
$startTime = microtime(true);
|
||||
$startMemory = memory_get_usage(true);
|
||||
|
||||
$options = new DiscoveryOptions(
|
||||
scanType: ScanType::FULL,
|
||||
paths: [$pathProvider->getBasePath() . '/src/Application/Admin'],
|
||||
useCache: false
|
||||
);
|
||||
|
||||
echo "Starting discovery process...\n";
|
||||
$registry = $discoveryService->discoverWithOptions($options);
|
||||
|
||||
$endTime = microtime(true);
|
||||
$endMemory = memory_get_usage(true);
|
||||
|
||||
echo "✅ Discovery completed\n";
|
||||
echo " Processing Time: " . round(($endTime - $startTime) * 1000, 2) . "ms\n";
|
||||
echo " Memory Used: " . round(($endMemory - $startMemory) / 1024 / 1024, 2) . "MB\n";
|
||||
echo " Registry Size: " . count($registry) . " items\n";
|
||||
|
||||
// Test 4: Event Statistics Analysis
|
||||
echo "\n🧪 Test 4: Event Statistics Analysis\n";
|
||||
|
||||
$finalStats = $eventAggregator->getStatistics();
|
||||
echo "✅ Final Event Statistics:\n";
|
||||
foreach ($finalStats as $key => $value) {
|
||||
echo " {$key}: {$value}\n";
|
||||
}
|
||||
|
||||
// Test 5: Comprehensive Analytics
|
||||
echo "\n🧪 Test 5: Comprehensive Event Analytics\n";
|
||||
|
||||
$analytics = $eventAggregator->getDiscoveryAnalytics();
|
||||
echo "✅ Discovery Analytics generated\n";
|
||||
|
||||
// Memory Pressure Analysis
|
||||
if (! empty($analytics['memory_pressure'])) {
|
||||
echo "\n📊 Memory Pressure Analysis:\n";
|
||||
$memoryAnalysis = $analytics['memory_pressure'];
|
||||
echo " Total Events: {$memoryAnalysis['total_events']}\n";
|
||||
echo " Critical Events: {$memoryAnalysis['critical_events']}\n";
|
||||
echo " Warning Events: {$memoryAnalysis['warning_events']}\n";
|
||||
echo " Average Pressure: " . round($memoryAnalysis['average_pressure'] * 100, 1) . "%\n";
|
||||
|
||||
if (! empty($memoryAnalysis['pressure_spikes'])) {
|
||||
echo " Pressure Spikes: " . count($memoryAnalysis['pressure_spikes']) . "\n";
|
||||
}
|
||||
} else {
|
||||
echo " No memory pressure events detected\n";
|
||||
}
|
||||
|
||||
// Cleanup Effectiveness Analysis
|
||||
if (! empty($analytics['cleanup_effectiveness'])) {
|
||||
echo "\n📊 Cleanup Effectiveness Analysis:\n";
|
||||
$cleanupAnalysis = $analytics['cleanup_effectiveness'];
|
||||
echo " Total Cleanups: {$cleanupAnalysis['total_cleanups']}\n";
|
||||
echo " Effective Cleanups: {$cleanupAnalysis['effective_cleanups']}\n";
|
||||
echo " Emergency Cleanups: {$cleanupAnalysis['emergency_cleanups']}\n";
|
||||
echo " Effectiveness Ratio: " . round($cleanupAnalysis['effectiveness_ratio'] * 100, 1) . "%\n";
|
||||
echo " Total Memory Freed: {$cleanupAnalysis['total_memory_freed']}\n";
|
||||
} else {
|
||||
echo " No cleanup events detected\n";
|
||||
}
|
||||
|
||||
// Memory Leak Analysis
|
||||
if (! empty($analytics['memory_leaks'])) {
|
||||
echo "\n📊 Memory Leak Analysis:\n";
|
||||
$leakAnalysis = $analytics['memory_leaks'];
|
||||
echo " Leaks Detected: {$leakAnalysis['total_leaks_detected']}\n";
|
||||
echo " Severity Distribution:\n";
|
||||
foreach ($leakAnalysis['severity_distribution'] as $severity => $count) {
|
||||
echo " {$severity}: {$count}\n";
|
||||
}
|
||||
|
||||
if (! empty($leakAnalysis['critical_leaks'])) {
|
||||
echo " Critical Leaks: " . count($leakAnalysis['critical_leaks']) . "\n";
|
||||
}
|
||||
} else {
|
||||
echo " No memory leaks detected\n";
|
||||
}
|
||||
|
||||
// Chunk Performance Analysis
|
||||
if (! empty($analytics['chunk_performance'])) {
|
||||
echo "\n📊 Chunk Performance Analysis:\n";
|
||||
$chunkAnalysis = $analytics['chunk_performance'];
|
||||
echo " Total Chunk Events: {$chunkAnalysis['total_chunk_events']}\n";
|
||||
echo " Completed Chunks: {$chunkAnalysis['completed_chunks']}\n";
|
||||
echo " Failed Chunks: {$chunkAnalysis['failed_chunks']}\n";
|
||||
echo " Memory Adjustments: {$chunkAnalysis['memory_adjustments']}\n";
|
||||
echo " Average Chunk Size: " . round($chunkAnalysis['average_chunk_size'], 1) . "\n";
|
||||
echo " Average Processing Time: " . round($chunkAnalysis['average_processing_time'], 3) . "s\n";
|
||||
echo " Success Rate: " . round($chunkAnalysis['success_rate'] * 100, 1) . "%\n";
|
||||
echo " Total Memory Usage: {$chunkAnalysis['total_memory_usage']}\n";
|
||||
|
||||
echo " Event Type Distribution:\n";
|
||||
foreach ($chunkAnalysis['event_type_distribution'] as $type => $count) {
|
||||
echo " {$type}: {$count}\n";
|
||||
}
|
||||
} else {
|
||||
echo " No chunk processing events detected\n";
|
||||
}
|
||||
|
||||
// Strategy Change Analysis
|
||||
if (! empty($analytics['strategy_changes'])) {
|
||||
echo "\n📊 Strategy Change Analysis:\n";
|
||||
$strategyAnalysis = $analytics['strategy_changes'];
|
||||
echo " Total Strategy Changes: {$strategyAnalysis['total_strategy_changes']}\n";
|
||||
echo " Emergency Changes: {$strategyAnalysis['emergency_changes']}\n";
|
||||
echo " Strategy Downgrades: {$strategyAnalysis['strategy_downgrades']}\n";
|
||||
echo " Emergency Ratio: " . round($strategyAnalysis['emergency_ratio'] * 100, 1) . "%\n";
|
||||
|
||||
echo " Strategy Usage:\n";
|
||||
foreach ($strategyAnalysis['strategy_usage'] as $strategy => $count) {
|
||||
echo " {$strategy}: {$count}\n";
|
||||
}
|
||||
} else {
|
||||
echo " No strategy changes detected\n";
|
||||
}
|
||||
|
||||
// Test 6: Event Pattern Recognition
|
||||
echo "\n🧪 Test 6: Event Pattern Recognition\n";
|
||||
|
||||
// Analyze event patterns over time
|
||||
$totalEvents = $finalStats['total_events'];
|
||||
if ($totalEvents > 0) {
|
||||
echo "✅ Event Pattern Analysis:\n";
|
||||
|
||||
$memoryEventRatio = round($finalStats['memory_pressure_events'] / $totalEvents * 100, 1);
|
||||
$cleanupEventRatio = round($finalStats['cleanup_events'] / $totalEvents * 100, 1);
|
||||
$chunkEventRatio = round($finalStats['chunk_events'] / $totalEvents * 100, 1);
|
||||
|
||||
echo " Memory Pressure Events: {$memoryEventRatio}% of all events\n";
|
||||
echo " Cleanup Events: {$cleanupEventRatio}% of all events\n";
|
||||
echo " Chunk Processing Events: {$chunkEventRatio}% of all events\n";
|
||||
|
||||
// Event intensity analysis
|
||||
$discoveryDuration = $endTime - $startTime;
|
||||
$eventsPerSecond = round($totalEvents / $discoveryDuration, 2);
|
||||
echo " Event Intensity: {$eventsPerSecond} events/second\n";
|
||||
|
||||
// Memory efficiency analysis
|
||||
if (! empty($analytics['memory_pressure']) && ! empty($analytics['cleanup_effectiveness'])) {
|
||||
$memoryEvents = $analytics['memory_pressure']['total_events'];
|
||||
$cleanupEvents = $analytics['cleanup_effectiveness']['total_cleanups'];
|
||||
|
||||
if ($memoryEvents > 0) {
|
||||
$cleanupResponseRatio = round($cleanupEvents / $memoryEvents, 2);
|
||||
echo " Cleanup Response Ratio: {$cleanupResponseRatio} cleanups per memory pressure event\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo " No events generated during discovery\n";
|
||||
}
|
||||
|
||||
// Test 7: Health Status with Event Information
|
||||
echo "\n🧪 Test 7: Health Status with Event Information\n";
|
||||
|
||||
$healthStatus = $discoveryService->getHealthStatus();
|
||||
echo "✅ Health Status Retrieved:\n";
|
||||
echo " Service Status: {$healthStatus['service']}\n";
|
||||
|
||||
if (isset($healthStatus['memory_management'])) {
|
||||
echo " Memory Status: {$healthStatus['memory_management']['status']}\n";
|
||||
echo " Memory Usage: {$healthStatus['memory_management']['current_usage']}\n";
|
||||
echo " Memory Strategy: {$healthStatus['memory_management']['strategy']}\n";
|
||||
echo " Guard Checks: {$healthStatus['memory_management']['guard_checks']}\n";
|
||||
echo " Emergency Mode: " . ($healthStatus['memory_management']['emergency_mode'] ? 'ON' : 'OFF') . "\n";
|
||||
}
|
||||
|
||||
echo "\n🎉 All event system tests completed successfully!\n";
|
||||
echo "\n🏆 Key Event System Features Verified:\n";
|
||||
echo " ✅ Memory pressure event detection and emission\n";
|
||||
echo " ✅ Memory cleanup event tracking with effectiveness metrics\n";
|
||||
echo " ✅ Memory leak detection with severity classification\n";
|
||||
echo " ✅ Chunk processing events with performance metrics\n";
|
||||
echo " ✅ Memory strategy change events with impact analysis\n";
|
||||
echo " ✅ Event aggregation and comprehensive analytics\n";
|
||||
echo " ✅ Real-time event statistics and monitoring\n";
|
||||
echo " ✅ Event pattern recognition and trend analysis\n";
|
||||
echo " ✅ Full Discovery service integration with events\n";
|
||||
echo " ✅ Health status reporting with event-based insights\n";
|
||||
|
||||
} catch (Throwable $e) {
|
||||
echo "\n❌ Error during event system testing:\n";
|
||||
echo " Message: " . $e->getMessage() . "\n";
|
||||
echo " File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
echo " Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
93
tests/debug/test-factory-configuration-debug.php
Normal file
93
tests/debug/test-factory-configuration-debug.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Debugging DiscoveryServiceFactory Configuration ===\n\n";
|
||||
|
||||
try {
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$srcPath = $projectRoot . '/src';
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
// Test 1: Check development configuration
|
||||
echo "=== Development Configuration ===\n";
|
||||
$devConfig = DiscoveryConfiguration::development();
|
||||
echo "Config array: " . json_encode($devConfig->toArray(), JSON_PRETTY_PRINT) . "\n";
|
||||
echo "Attribute mappers count: " . count($devConfig->attributeMappers) . "\n";
|
||||
echo "Target interfaces count: " . count($devConfig->targetInterfaces) . "\n\n";
|
||||
|
||||
// Test 2: Check factory
|
||||
echo "=== Factory Analysis ===\n";
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
|
||||
// Create service and debug
|
||||
$devConfigWithPaths = $devConfig->withPaths([$srcPath]);
|
||||
echo "Config with paths - Paths count: " . count($devConfigWithPaths->paths) . "\n";
|
||||
echo "Config with paths - Mappers count: " . count($devConfigWithPaths->attributeMappers) . "\n\n";
|
||||
|
||||
// Test 3: Direct reflection on the factory's buildAttributeMappers method
|
||||
echo "=== Reflecting Factory's buildAttributeMappers ===\n";
|
||||
$reflection = new ReflectionClass($factory);
|
||||
$buildMappersMethod = $reflection->getMethod('buildAttributeMappers');
|
||||
$buildMappersMethod->setAccessible(true);
|
||||
|
||||
// Call with empty array (what happens in development config)
|
||||
$emptyMappers = $buildMappersMethod->invoke($factory, []);
|
||||
echo "buildAttributeMappers([]) returns " . count($emptyMappers) . " mappers:\n";
|
||||
foreach ($emptyMappers as $mapper) {
|
||||
echo "- " . get_class($mapper) . "\n";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// Test 4: Test discovery with this factory
|
||||
echo "=== Testing Discovery with Factory ===\n";
|
||||
$discoveryService = $factory->createForDevelopment([$srcPath]);
|
||||
|
||||
// Get the actual configuration used
|
||||
$reflectionService = new ReflectionClass($discoveryService);
|
||||
$configProperty = $reflectionService->getProperty('configuration');
|
||||
$configProperty->setAccessible(true);
|
||||
$actualConfig = $configProperty->getValue($discoveryService);
|
||||
|
||||
$mappersProperty = $reflectionService->getProperty('attributeMappers');
|
||||
$mappersProperty->setAccessible(true);
|
||||
$actualMappers = $mappersProperty->getValue($discoveryService);
|
||||
|
||||
echo "UnifiedDiscoveryService configuration:\n";
|
||||
echo "- Config paths: " . count($actualConfig->paths) . "\n";
|
||||
echo "- Config mappers: " . count($actualConfig->attributeMappers) . "\n";
|
||||
echo "- Service mappers: " . count($actualMappers) . "\n";
|
||||
|
||||
echo "\nService mappers:\n";
|
||||
foreach ($actualMappers as $mapper) {
|
||||
echo "- " . get_class($mapper) . "\n";
|
||||
}
|
||||
|
||||
echo "\n=== Running Discovery ===\n";
|
||||
$registry = $discoveryService->discover();
|
||||
|
||||
echo "Results:\n";
|
||||
echo "- Total: " . count($registry) . "\n";
|
||||
echo "- Initializers: " . $registry->attributes->getCount('App\\Framework\\DI\\Initializer') . "\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
114
tests/debug/test-factory-vs-direct-discovery.php
Normal file
114
tests/debug/test-factory-vs-direct-discovery.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\DI\Initializer;
|
||||
use App\Framework\DI\InitializerMapper;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Discovery\UnifiedDiscoveryService;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Comparing Factory vs Direct Discovery ===\n\n";
|
||||
|
||||
try {
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$srcPath = $projectRoot . '/src';
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
echo "=== Method 1: Using DiscoveryServiceFactory ===\n";
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
$discoveryService1 = $factory->createForDevelopment([$srcPath]);
|
||||
|
||||
$registry1 = $discoveryService1->discover();
|
||||
$initializers1 = $registry1->attributes->get(Initializer::class);
|
||||
|
||||
echo "Factory-based discovery:\n";
|
||||
echo "- Total items: " . count($registry1) . "\n";
|
||||
echo "- Initializers: " . count($initializers1) . "\n";
|
||||
echo "- Attribute types: " . count($registry1->attributes->getAllTypes()) . "\n\n";
|
||||
|
||||
echo "=== Method 2: Direct UnifiedDiscoveryService ===\n";
|
||||
$fileSystemService = new FileSystemService();
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
|
||||
$config = new DiscoveryConfiguration(
|
||||
paths: [$srcPath],
|
||||
attributeMappers: [new InitializerMapper()],
|
||||
targetInterfaces: [],
|
||||
useCache: false
|
||||
);
|
||||
|
||||
$discoveryService2 = new UnifiedDiscoveryService(
|
||||
pathProvider: $pathProvider,
|
||||
cache: $cache,
|
||||
clock: $clock,
|
||||
reflectionProvider: $reflectionProvider,
|
||||
configuration: $config,
|
||||
attributeMappers: [new InitializerMapper()],
|
||||
targetInterfaces: []
|
||||
);
|
||||
|
||||
$registry2 = $discoveryService2->discover();
|
||||
$initializers2 = $registry2->attributes->get(Initializer::class);
|
||||
|
||||
echo "Direct discovery:\n";
|
||||
echo "- Total items: " . count($registry2) . "\n";
|
||||
echo "- Initializers: " . count($initializers2) . "\n";
|
||||
echo "- Attribute types: " . count($registry2->attributes->getAllTypes()) . "\n\n";
|
||||
|
||||
echo "=== Detailed Comparison ===\n";
|
||||
echo "Factory found " . count($initializers1) . " initializers:\n";
|
||||
foreach ($initializers1 as $mapping) {
|
||||
$className = $mapping->class->getShortName();
|
||||
$methodName = $mapping->method ? $mapping->method->toString() : '(class)';
|
||||
echo "- $className::$methodName\n";
|
||||
}
|
||||
|
||||
echo "\nDirect found " . count($initializers2) . " initializers:\n";
|
||||
foreach ($initializers2 as $mapping) {
|
||||
$className = $mapping->class->getShortName();
|
||||
$methodName = $mapping->method ? $mapping->method->toString() : '(class)';
|
||||
echo "- $className::$methodName\n";
|
||||
}
|
||||
|
||||
// Check configuration differences
|
||||
echo "\n=== Configuration Analysis ===\n";
|
||||
|
||||
// Get factory configuration somehow
|
||||
echo "Factory configuration: Using multiple mappers from buildAttributeMappers()\n";
|
||||
echo "Direct configuration: Only InitializerMapper\n";
|
||||
|
||||
echo "\nFactory attribute types found:\n";
|
||||
foreach ($registry1->attributes->getAllTypes() as $type) {
|
||||
$shortType = substr(strrchr($type, '\\'), 1) ?: $type;
|
||||
$count = $registry1->attributes->getCount($type);
|
||||
echo "- $shortType: $count items\n";
|
||||
}
|
||||
|
||||
echo "\nDirect attribute types found:\n";
|
||||
foreach ($registry2->attributes->getAllTypes() as $type) {
|
||||
$shortType = substr(strrchr($type, '\\'), 1) ?: $type;
|
||||
$count = $registry2->attributes->getCount($type);
|
||||
echo "- $shortType: $count items\n";
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
117
tests/debug/test-initializer-discovery-details.php
Normal file
117
tests/debug/test-initializer-discovery-details.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\DI\Initializer;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Debugging Initializer Discovery Details ===\n\n";
|
||||
|
||||
try {
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$srcPath = $projectRoot . '/src';
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
$discoveryService = $factory->createForDevelopment([$srcPath]);
|
||||
|
||||
echo "Running discovery...\n";
|
||||
$registry = $discoveryService->discover();
|
||||
|
||||
echo "Total items discovered: " . count($registry) . "\n";
|
||||
echo "Total Initializers found: " . $registry->attributes->getCount(Initializer::class) . "\n\n";
|
||||
|
||||
// Get all initializers
|
||||
$initializers = $registry->attributes->get(Initializer::class);
|
||||
|
||||
echo "=== Analyzing Each Initializer ===\n";
|
||||
$counter = 0;
|
||||
foreach ($initializers as $mapping) {
|
||||
$counter++;
|
||||
$className = $mapping->class->getFullyQualified();
|
||||
$methodName = $mapping->method ? $mapping->method->toString() : '(class)';
|
||||
|
||||
echo "\n[$counter] $className::$methodName\n";
|
||||
|
||||
// Check mapped data
|
||||
if ($mapping->mappedData) {
|
||||
echo " Mapped Data:\n";
|
||||
foreach ($mapping->mappedData as $key => $value) {
|
||||
if ($key === 'contexts') {
|
||||
if ($value === null) {
|
||||
echo " - contexts: NULL ⚠️ (will be skipped!)\n";
|
||||
} elseif (is_array($value)) {
|
||||
echo " - contexts: [" . implode(', ', array_map(fn ($c) => $c?->value ?? 'null', $value)) . "]\n";
|
||||
} else {
|
||||
echo " - contexts: " . json_encode($value) . "\n";
|
||||
}
|
||||
} else {
|
||||
echo " - $key: " . (is_string($value) ? $value : json_encode($value)) . "\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo " ❌ No mapped data!\n";
|
||||
}
|
||||
|
||||
// Check attributes
|
||||
if ($mapping->attributes) {
|
||||
echo " Raw Attributes:\n";
|
||||
foreach ($mapping->attributes as $attr) {
|
||||
if ($attr instanceof Initializer) {
|
||||
$reflection = new ReflectionObject($attr);
|
||||
$props = $reflection->getProperties();
|
||||
foreach ($props as $prop) {
|
||||
$prop->setAccessible(true);
|
||||
$val = $prop->getValue($attr);
|
||||
if ($prop->getName() === 'contexts') {
|
||||
if ($val === null) {
|
||||
echo " - contexts (raw): NULL\n";
|
||||
} elseif (is_array($val)) {
|
||||
echo " - contexts (raw): [" . implode(', ', array_map(fn ($c) => $c?->value ?? 'null', $val)) . "]\n";
|
||||
} else {
|
||||
echo " - contexts (raw): " . json_encode($val) . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n=== Summary ===\n";
|
||||
echo "Total Initializers analyzed: $counter\n";
|
||||
|
||||
// Count how many have null contexts
|
||||
$nullContexts = 0;
|
||||
$withContexts = 0;
|
||||
foreach ($initializers as $mapping) {
|
||||
if ($mapping->mappedData && isset($mapping->mappedData['contexts'])) {
|
||||
if ($mapping->mappedData['contexts'] === null) {
|
||||
$nullContexts++;
|
||||
} else {
|
||||
$withContexts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Initializers with NULL contexts: $nullContexts (will be skipped)\n";
|
||||
echo "Initializers with valid contexts: $withContexts\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
132
tests/debug/test-initializer-discovery.php
Normal file
132
tests/debug/test-initializer-discovery.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\DI\InitializerMapper;
|
||||
use App\Framework\Discovery\UnifiedDiscoveryService;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Testing Initializer Discovery ===\n\n";
|
||||
|
||||
try {
|
||||
// Setup with correct path
|
||||
$projectRoot = dirname(__DIR__, 2); // Go up from tests/debug to project root
|
||||
$srcPath = $projectRoot . '/src';
|
||||
|
||||
echo "Project root: $projectRoot\n";
|
||||
echo "Source path: $srcPath\n";
|
||||
echo "Source path exists: " . (is_dir($srcPath) ? 'Yes' : 'No') . "\n\n";
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
$fileSystemService = new FileSystemService();
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
|
||||
// Create cache for discovery
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
// Create InitializerMapper
|
||||
$initializerMapper = new InitializerMapper();
|
||||
|
||||
// Configure discovery for the entire src directory
|
||||
$config = new DiscoveryConfiguration(
|
||||
paths: [$srcPath],
|
||||
attributeMappers: [$initializerMapper],
|
||||
targetInterfaces: [],
|
||||
useCache: false
|
||||
);
|
||||
|
||||
// Create discovery service
|
||||
$discoveryService = new UnifiedDiscoveryService(
|
||||
pathProvider: $pathProvider,
|
||||
cache: $cache,
|
||||
clock: $clock,
|
||||
reflectionProvider: $reflectionProvider,
|
||||
configuration: $config,
|
||||
attributeMappers: [$initializerMapper],
|
||||
targetInterfaces: []
|
||||
);
|
||||
|
||||
echo "Starting discovery scan...\n";
|
||||
echo "Scanning path: $srcPath\n";
|
||||
echo "Path exists: " . (is_dir($srcPath) ? 'Yes' : 'No') . "\n";
|
||||
|
||||
// Check if path has PHP files
|
||||
$phpFiles = glob($srcPath . '/**/*.php', GLOB_BRACE);
|
||||
echo "PHP files found in path: " . count($phpFiles) . "\n";
|
||||
if (count($phpFiles) > 0) {
|
||||
echo "First few files:\n";
|
||||
foreach (array_slice($phpFiles, 0, 5) as $file) {
|
||||
echo " - $file\n";
|
||||
}
|
||||
}
|
||||
|
||||
$registry = $discoveryService->discover();
|
||||
|
||||
echo "\nDiscovery completed!\n\n";
|
||||
|
||||
echo "=== Discovery Results ===\n";
|
||||
echo "Total registry count: " . count($registry) . "\n";
|
||||
echo "Attributes registry count: " . count($registry->attributes) . "\n\n";
|
||||
|
||||
// Check for Initializers in attribute registry
|
||||
$initializerAttributeClass = 'App\\Framework\\DI\\Initializer';
|
||||
|
||||
// Check if the attribute registry has the method
|
||||
if (method_exists($registry->attributes, 'get')) {
|
||||
$initializers = $registry->attributes->get($initializerAttributeClass);
|
||||
|
||||
echo "=== Initializer Results ===\n";
|
||||
echo "Initializers found: " . count($initializers) . "\n\n";
|
||||
|
||||
if (empty($initializers)) {
|
||||
echo "❌ No initializers found!\n\n";
|
||||
|
||||
// Try to show what's actually in the registry
|
||||
echo "=== Debug: Registry Structure ===\n";
|
||||
echo "Registry class: " . get_class($registry) . "\n";
|
||||
echo "Attributes class: " . get_class($registry->attributes) . "\n";
|
||||
|
||||
// Check if we can iterate
|
||||
if (method_exists($registry->attributes, 'getAllTypes')) {
|
||||
echo "\n=== Available Attribute Types ===\n";
|
||||
$allAttributes = $registry->attributes->getAllTypes();
|
||||
foreach ($allAttributes as $attrClass) {
|
||||
$count = count($registry->attributes->get($attrClass));
|
||||
echo "- $attrClass: $count items\n";
|
||||
}
|
||||
} else {
|
||||
echo "Method getAllTypes not available\n";
|
||||
}
|
||||
} else {
|
||||
echo "✅ Found initializers:\n";
|
||||
foreach ($initializers as $mapping) {
|
||||
$className = $mapping->class->getFullyQualified();
|
||||
$methodName = $mapping->method ? $mapping->method->toString() : '(class-level)';
|
||||
echo "- {$className}::{$methodName}\n";
|
||||
echo " File: " . $mapping->file->toString() . "\n";
|
||||
echo " Target: " . $mapping->target . "\n";
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "❌ AttributeRegistry doesn't have 'get' method\n";
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
109
tests/debug/test-initializer-execution.php
Normal file
109
tests/debug/test-initializer-execution.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Context\ExecutionContext;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\DiscoveryServiceBootstrapper;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Testing Initializer Execution ===\n\n";
|
||||
|
||||
try {
|
||||
// Setup with correct path
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
|
||||
echo "Project root: $projectRoot\n";
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
|
||||
// Create cache for discovery
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
// Register required dependencies
|
||||
$container->instance(PathProvider::class, $pathProvider);
|
||||
$container->instance(\App\Framework\Cache\CacheDriver::class, $cacheDriver);
|
||||
$container->instance(\App\Framework\Serializer\Serializer::class, $serializer);
|
||||
$container->instance(\App\Framework\Cache\Cache::class, $cache);
|
||||
$container->instance(\App\Framework\DateTime\Clock::class, $clock);
|
||||
$container->instance(\App\Framework\Reflection\ReflectionProvider::class, new CachedReflectionProvider());
|
||||
|
||||
echo "Current execution context: " . ExecutionContext::detect()->getType()->value . "\n\n";
|
||||
|
||||
// Create bootstrapper
|
||||
$bootstrapper = new DiscoveryServiceBootstrapper($container, $clock);
|
||||
|
||||
echo "Starting bootstrap process...\n";
|
||||
|
||||
// This should discover AND execute initializers
|
||||
$registry = $bootstrapper->bootstrap();
|
||||
|
||||
echo "\nBootstrap completed!\n\n";
|
||||
|
||||
echo "=== Registry Results ===\n";
|
||||
echo "Total registry count: " . count($registry) . "\n";
|
||||
echo "Attributes registry count: " . count($registry->attributes) . "\n\n";
|
||||
|
||||
// Check what initializers were found
|
||||
$initializerAttributeClass = 'App\\Framework\\DI\\Initializer';
|
||||
$initializers = $registry->attributes->get($initializerAttributeClass);
|
||||
|
||||
echo "=== Initializer Results ===\n";
|
||||
echo "Initializers found: " . count($initializers) . "\n\n";
|
||||
|
||||
if (! empty($initializers)) {
|
||||
echo "✅ Found initializers:\n";
|
||||
foreach (array_slice($initializers, 0, 5) as $mapping) {
|
||||
$className = $mapping->class->getFullyQualified();
|
||||
$methodName = $mapping->method ? $mapping->method->toString() : '(class-level)';
|
||||
echo "- {$className}::{$methodName}\n";
|
||||
echo " Target: " . $mapping->target . "\n";
|
||||
|
||||
// Check the mapped data structure
|
||||
if ($mapping->mappedData) {
|
||||
echo " Mapped Data Keys: " . implode(', ', array_keys($mapping->mappedData)) . "\n";
|
||||
if (isset($mapping->mappedData['return'])) {
|
||||
echo " Return Type: " . ($mapping->mappedData['return'] ?? 'null') . "\n";
|
||||
}
|
||||
if (isset($mapping->mappedData['contexts'])) {
|
||||
echo " Contexts: " . json_encode($mapping->mappedData['contexts']) . "\n";
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Check what's in the container now
|
||||
echo "=== Container Status ===\n";
|
||||
|
||||
// Check if some key services were registered
|
||||
$expectedServices = [
|
||||
'App\\Framework\\Cache\\Cache',
|
||||
'App\\Framework\\Logging\\Logger',
|
||||
'App\\Framework\\Database\\EntityManager',
|
||||
'App\\Framework\\Http\\Session\\SessionManager',
|
||||
'App\\Framework\\View\\TemplateRenderer',
|
||||
'App\\Framework\\Performance\\PerformanceCollectorInterface',
|
||||
'App\\Framework\\Discovery\\Results\\DiscoveryRegistry',
|
||||
];
|
||||
|
||||
foreach ($expectedServices as $service) {
|
||||
$exists = $container->has($service);
|
||||
echo "- $service: " . ($exists ? "✅ Registered" : "❌ Missing") . "\n";
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
66
tests/debug/test-initializers.php
Normal file
66
tests/debug/test-initializers.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
try {
|
||||
echo "=== Initializer Discovery Test ===\n";
|
||||
|
||||
// Create basic dependencies
|
||||
$container = new DefaultContainer();
|
||||
$clock = new SystemClock();
|
||||
$pathProvider = new PathProvider('/var/www/html');
|
||||
|
||||
// Create simple cache
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
// Create discovery factory
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
|
||||
// Create discovery service for development
|
||||
$discoveryService = $factory->createForDevelopment();
|
||||
|
||||
// Run discovery
|
||||
$registry = $discoveryService->discover();
|
||||
|
||||
$initializers = $registry->attributes->get('App\\Framework\\DI\\Initializer');
|
||||
echo 'Initializers found: ' . count($initializers) . "\n";
|
||||
|
||||
foreach ($initializers as $init) {
|
||||
echo ' - ' . $init->class->getFullyQualified() . '::' . ($init->method ? $init->method->toString() : 'class') . "\n";
|
||||
if ($init->mappedData) {
|
||||
echo ' Data: ' . json_encode($init->mappedData) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Check if CacheInitializer is specifically found
|
||||
$cacheInitializerFound = false;
|
||||
foreach ($initializers as $init) {
|
||||
if (str_contains($init->class->getFullyQualified(), 'CacheInitializer')) {
|
||||
$cacheInitializerFound = true;
|
||||
echo "\n✅ CacheInitializer found!\n";
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $cacheInitializerFound) {
|
||||
echo "\n❌ CacheInitializer NOT found!\n";
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
echo "ERROR: " . $e->getMessage() . "\n";
|
||||
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
echo "Trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
282
tests/debug/test-integrated-discovery-cache.php
Normal file
282
tests/debug/test-integrated-discovery-cache.php
Normal file
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Test script for Integrated Discovery Cache Manager
|
||||
*
|
||||
* Tests the enhanced DiscoveryCacheManager with memory-aware and tiered caching
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\Events\EventDispatcher;
|
||||
use App\Framework\Core\ValueObjects\Byte;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\Discovery\Memory\DiscoveryMemoryManager;
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Discovery\Storage\DiscoveryCacheManager;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryContext;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryOptions;
|
||||
use App\Framework\Discovery\ValueObjects\MemoryStrategy;
|
||||
use App\Framework\Discovery\ValueObjects\ScanType;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Logging\DefaultLogger;
|
||||
use App\Framework\Logging\Handlers\ConsoleHandler;
|
||||
use App\Framework\Logging\LogLevel;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
|
||||
echo "🧪 Testing Integrated Discovery Cache Manager\n";
|
||||
echo "============================================\n";
|
||||
|
||||
try {
|
||||
// Setup basic dependencies
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new PhpSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
$clock = new SystemClock();
|
||||
$eventDispatcher = new EventDispatcher();
|
||||
$fileSystemService = new FileSystemService();
|
||||
|
||||
// Setup logging
|
||||
$consoleHandler = new ConsoleHandler();
|
||||
$logger = new DefaultLogger(LogLevel::INFO, [$consoleHandler]);
|
||||
|
||||
echo "✅ Basic dependencies initialized\n";
|
||||
|
||||
// Test 1: Standard Cache Manager (without memory management)
|
||||
echo "\n🧪 Test 1: Standard Cache Manager (Legacy Mode)\n";
|
||||
|
||||
$basicCacheManager = new DiscoveryCacheManager(
|
||||
cache: $cache,
|
||||
clock: $clock,
|
||||
fileSystemService: $fileSystemService,
|
||||
logger: $logger,
|
||||
ttlHours: 1
|
||||
);
|
||||
|
||||
// Create test context and registry
|
||||
$testContext = new DiscoveryContext(
|
||||
paths: ['/test/path'],
|
||||
scanType: ScanType::FULL,
|
||||
options: new DiscoveryOptions(),
|
||||
startTime: $clock->now()
|
||||
);
|
||||
|
||||
$testRegistry = new DiscoveryRegistry();
|
||||
// Add some test data to registry
|
||||
$testRegistry->addRoute('/test/route', 'TestController::index');
|
||||
$testRegistry->addRoute('/api/test', 'ApiController::test');
|
||||
|
||||
// Test basic store/retrieve
|
||||
$stored = $basicCacheManager->store($testContext, $testRegistry);
|
||||
echo " Standard storage: " . ($stored ? '✅' : '❌') . "\n";
|
||||
|
||||
$retrieved = $basicCacheManager->get($testContext);
|
||||
$retrieveSuccess = $retrieved !== null && count($retrieved) === count($testRegistry);
|
||||
echo " Standard retrieval: " . ($retrieveSuccess ? '✅' : '❌') . "\n";
|
||||
|
||||
$healthStatus = $basicCacheManager->getHealthStatus();
|
||||
echo " Memory-aware: " . ($healthStatus['memory_aware'] ? 'enabled' : 'disabled') . "\n";
|
||||
|
||||
// Test 2: Enhanced Cache Manager with Memory Management
|
||||
echo "\n🧪 Test 2: Enhanced Cache Manager (Memory-Aware Mode)\n";
|
||||
|
||||
// Setup memory management
|
||||
$memoryLimit = Byte::fromMegabytes(16); // Small limit for testing
|
||||
$strategy = MemoryStrategy::ADAPTIVE;
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
|
||||
$memoryManager = new DiscoveryMemoryManager(
|
||||
strategy: $strategy,
|
||||
memoryLimit: $memoryLimit,
|
||||
memoryPressureThreshold: 0.5, // Low threshold for testing
|
||||
memoryMonitor: $memoryMonitor,
|
||||
logger: $logger,
|
||||
eventDispatcher: $eventDispatcher,
|
||||
clock: $clock
|
||||
);
|
||||
|
||||
$enhancedCacheManager = new DiscoveryCacheManager(
|
||||
cache: $cache,
|
||||
clock: $clock,
|
||||
fileSystemService: $fileSystemService,
|
||||
logger: $logger,
|
||||
ttlHours: 2,
|
||||
memoryManager: $memoryManager,
|
||||
eventDispatcher: $eventDispatcher,
|
||||
compressionThreshold: 0.3, // Low threshold for testing
|
||||
evictionThreshold: 0.6
|
||||
);
|
||||
|
||||
echo "✅ Enhanced cache manager created with memory management\n";
|
||||
|
||||
// Test memory-aware storage
|
||||
$enhancedStored = $enhancedCacheManager->store($testContext, $testRegistry);
|
||||
echo " Memory-aware storage: " . ($enhancedStored ? '✅' : '❌') . "\n";
|
||||
|
||||
$enhancedRetrieved = $enhancedCacheManager->get($testContext);
|
||||
$enhancedRetrieveSuccess = $enhancedRetrieved !== null && count($enhancedRetrieved) === count($testRegistry);
|
||||
echo " Memory-aware retrieval: " . ($enhancedRetrieveSuccess ? '✅' : '❌') . "\n";
|
||||
|
||||
// Test health status with memory info
|
||||
$enhancedHealthStatus = $enhancedCacheManager->getHealthStatus();
|
||||
echo " Memory-aware: " . ($enhancedHealthStatus['memory_aware'] ? 'enabled' : 'disabled') . "\n";
|
||||
if (isset($enhancedHealthStatus['memory_management'])) {
|
||||
echo " Memory status: " . $enhancedHealthStatus['memory_management']['status'] . "\n";
|
||||
echo " Memory pressure: " . $enhancedHealthStatus['memory_management']['memory_pressure'] . "\n";
|
||||
echo " Cache level: " . $enhancedHealthStatus['memory_management']['cache_level'] . "\n";
|
||||
}
|
||||
|
||||
// Test 3: Cache Metrics
|
||||
echo "\n🧪 Test 3: Cache Metrics & Analytics\n";
|
||||
|
||||
$cacheMetrics = $enhancedCacheManager->getCacheMetrics();
|
||||
if ($cacheMetrics !== null) {
|
||||
echo "✅ Cache metrics available\n";
|
||||
echo " Total items: " . $cacheMetrics->totalItems . "\n";
|
||||
echo " Hit rate: " . $cacheMetrics->hitRate->toPercentage()->toString() . "\n";
|
||||
echo " Total size: " . $cacheMetrics->totalSize->toHumanReadable() . "\n";
|
||||
echo " Compression ratio: " . $cacheMetrics->compressionRatio->toPercentage()->toString() . "\n";
|
||||
echo " Health status: " . $cacheMetrics->getHealthStatus() . "\n";
|
||||
echo " Quality score: " . $cacheMetrics->getQualityScore()->toString() . "\n";
|
||||
echo " Memory pressure impact: " . $cacheMetrics->getMemoryPressureImpact() . "\n";
|
||||
|
||||
$recommendations = $cacheMetrics->getRecommendedActions();
|
||||
if (! empty($recommendations)) {
|
||||
echo " Recommendations:\n";
|
||||
foreach ($recommendations as $recommendation) {
|
||||
echo " - " . $recommendation . "\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo " Cache metrics: not available (expected for basic manager)\n";
|
||||
}
|
||||
|
||||
// Test 4: Memory Pressure Management
|
||||
echo "\n🧪 Test 4: Memory Pressure Management\n";
|
||||
|
||||
$managementResult = $enhancedCacheManager->performMemoryPressureManagement();
|
||||
echo "✅ Memory pressure management executed\n";
|
||||
echo " Actions performed: " . count($managementResult['actions']) . "\n";
|
||||
if (! empty($managementResult['actions'])) {
|
||||
foreach ($managementResult['actions'] as $action) {
|
||||
echo " - " . $action . "\n";
|
||||
}
|
||||
}
|
||||
echo " Cache level: " . $managementResult['cache_level'] . "\n";
|
||||
|
||||
// Test 5: Large Data Processing (to trigger compression)
|
||||
echo "\n🧪 Test 5: Large Data Processing & Compression\n";
|
||||
|
||||
// Create larger registry to trigger compression
|
||||
$largeRegistry = new DiscoveryRegistry();
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$largeRegistry->addRoute("/large/route/{$i}", "LargeController::method{$i}");
|
||||
$largeRegistry->addMiddleware("Middleware{$i}", "middleware.{$i}");
|
||||
}
|
||||
|
||||
$largeContext = new DiscoveryContext(
|
||||
paths: ['/large/test/path'],
|
||||
scanType: ScanType::FULL,
|
||||
options: new DiscoveryOptions(),
|
||||
startTime: $clock->now()
|
||||
);
|
||||
|
||||
$largeStored = $enhancedCacheManager->store($largeContext, $largeRegistry);
|
||||
echo " Large data storage: " . ($largeStored ? '✅' : '❌') . "\n";
|
||||
|
||||
$largeRetrieved = $enhancedCacheManager->get($largeContext);
|
||||
$largeRetrieveSuccess = $largeRetrieved !== null && count($largeRetrieved) === count($largeRegistry);
|
||||
echo " Large data retrieval: " . ($largeRetrieveSuccess ? '✅' : '❌') . "\n";
|
||||
|
||||
// Check if compression was applied
|
||||
$updatedMetrics = $enhancedCacheManager->getCacheMetrics();
|
||||
if ($updatedMetrics !== null) {
|
||||
echo " Updated total size: " . $updatedMetrics->totalSize->toHumanReadable() . "\n";
|
||||
echo " Compression effectiveness: " . $updatedMetrics->getCompressionEffectiveness() . "\n";
|
||||
}
|
||||
|
||||
// Test 6: Cache Invalidation
|
||||
echo "\n🧪 Test 6: Cache Invalidation\n";
|
||||
|
||||
$invalidated = $enhancedCacheManager->invalidate($testContext);
|
||||
echo " Context invalidation: " . ($invalidated ? '✅' : '❌') . "\n";
|
||||
|
||||
$afterInvalidation = $enhancedCacheManager->get($testContext);
|
||||
$invalidationWorked = $afterInvalidation === null;
|
||||
echo " Invalidation verification: " . ($invalidationWorked ? '✅' : '❌') . "\n";
|
||||
|
||||
// Test 7: Integration Testing with Different Memory Pressures
|
||||
echo "\n🧪 Test 7: Memory Pressure Integration Testing\n";
|
||||
|
||||
// Simulate different memory pressures
|
||||
$pressureTests = [
|
||||
['name' => 'Low Pressure', 'limit' => Byte::fromMegabytes(100)],
|
||||
['name' => 'Medium Pressure', 'limit' => Byte::fromMegabytes(32)],
|
||||
['name' => 'High Pressure', 'limit' => Byte::fromMegabytes(8)],
|
||||
];
|
||||
|
||||
foreach ($pressureTests as $test) {
|
||||
$pressureMemoryManager = new DiscoveryMemoryManager(
|
||||
strategy: MemoryStrategy::CONSERVATIVE,
|
||||
memoryLimit: $test['limit'],
|
||||
memoryPressureThreshold: 0.4,
|
||||
memoryMonitor: $memoryMonitor,
|
||||
logger: $logger,
|
||||
eventDispatcher: $eventDispatcher,
|
||||
clock: $clock
|
||||
);
|
||||
|
||||
$pressureCacheManager = new DiscoveryCacheManager(
|
||||
cache: $cache,
|
||||
clock: $clock,
|
||||
fileSystemService: $fileSystemService,
|
||||
logger: $logger,
|
||||
ttlHours: 1,
|
||||
memoryManager: $pressureMemoryManager,
|
||||
eventDispatcher: $eventDispatcher
|
||||
);
|
||||
|
||||
$pressureContext = new DiscoveryContext(
|
||||
paths: ["/pressure/{$test['name']}"],
|
||||
scanType: ScanType::FULL,
|
||||
options: new DiscoveryOptions(),
|
||||
startTime: $clock->now()
|
||||
);
|
||||
|
||||
$pressureStored = $pressureCacheManager->store($pressureContext, $largeRegistry);
|
||||
$pressureHealthStatus = $pressureCacheManager->getHealthStatus();
|
||||
|
||||
echo " {$test['name']} ({$test['limit']->toHumanReadable()}):\n";
|
||||
echo " Storage: " . ($pressureStored ? '✅' : '❌') . "\n";
|
||||
if (isset($pressureHealthStatus['memory_management'])) {
|
||||
echo " Memory status: " . $pressureHealthStatus['memory_management']['status'] . "\n";
|
||||
echo " Cache level: " . $pressureHealthStatus['memory_management']['cache_level'] . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n🎉 All integrated cache manager tests completed successfully!\n";
|
||||
echo "\n🏆 Key Integration Features Verified:\n";
|
||||
echo " ✅ Backward compatibility with standard caching\n";
|
||||
echo " ✅ Optional memory-aware enhancement\n";
|
||||
echo " ✅ Automatic tiered caching with compression\n";
|
||||
echo " ✅ Memory pressure-based cache management\n";
|
||||
echo " ✅ Comprehensive cache metrics with Score objects\n";
|
||||
echo " ✅ Event-driven cache monitoring\n";
|
||||
echo " ✅ Intelligent cache level adjustment\n";
|
||||
echo " ✅ Large data handling with compression\n";
|
||||
echo " ✅ Cache invalidation and cleanup\n";
|
||||
echo " ✅ Multi-pressure scenario handling\n";
|
||||
echo "\n🔄 No Functionality Duplication - Single Enhanced Manager!\n";
|
||||
|
||||
} catch (Throwable $e) {
|
||||
echo "\n❌ Error during integrated cache testing:\n";
|
||||
echo " Message: " . $e->getMessage() . "\n";
|
||||
echo " File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
echo " Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
248
tests/debug/test-memory-aware-caching.php
Normal file
248
tests/debug/test-memory-aware-caching.php
Normal file
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Test script for Memory-Aware Caching System
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\Events\EventDispatcher;
|
||||
use App\Framework\Core\ValueObjects\Byte;
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\Discovery\Cache\MemoryAwareCacheManager;
|
||||
use App\Framework\Discovery\Cache\TieredCacheManager;
|
||||
use App\Framework\Discovery\Memory\DiscoveryMemoryManager;
|
||||
use App\Framework\Discovery\ValueObjects\CacheLevel;
|
||||
use App\Framework\Discovery\ValueObjects\CacheTier;
|
||||
use App\Framework\Discovery\ValueObjects\CompressionLevel;
|
||||
use App\Framework\Discovery\ValueObjects\MemoryStrategy;
|
||||
use App\Framework\Logging\DefaultLogger;
|
||||
use App\Framework\Logging\Handlers\ConsoleHandler;
|
||||
use App\Framework\Logging\LogLevel;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
|
||||
echo "🧪 Testing Memory-Aware Caching System\n";
|
||||
echo "=====================================\n";
|
||||
|
||||
try {
|
||||
// Setup basic dependencies
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new PhpSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
$clock = new SystemClock();
|
||||
$eventDispatcher = new EventDispatcher();
|
||||
|
||||
// Setup logging
|
||||
$consoleHandler = new ConsoleHandler();
|
||||
$logger = new DefaultLogger(LogLevel::INFO, [$consoleHandler]);
|
||||
|
||||
// Setup memory management
|
||||
$memoryLimit = Byte::fromMegabytes(32); // Small limit to trigger events
|
||||
$strategy = MemoryStrategy::ADAPTIVE;
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
|
||||
$memoryManager = new DiscoveryMemoryManager(
|
||||
strategy: $strategy,
|
||||
memoryLimit: $memoryLimit,
|
||||
memoryPressureThreshold: 0.6, // Lower threshold for testing
|
||||
memoryMonitor: $memoryMonitor,
|
||||
logger: $logger,
|
||||
eventDispatcher: $eventDispatcher,
|
||||
clock: $clock
|
||||
);
|
||||
|
||||
echo "✅ Dependencies initialized\n";
|
||||
|
||||
// Test 1: Memory-Aware Cache Manager
|
||||
echo "\n🧪 Test 1: Memory-Aware Cache Manager\n";
|
||||
|
||||
$memoryAwareCache = new MemoryAwareCacheManager(
|
||||
cache: $cache,
|
||||
memoryManager: $memoryManager,
|
||||
clock: $clock,
|
||||
logger: $logger,
|
||||
eventDispatcher: $eventDispatcher,
|
||||
defaultTtl: Duration::fromMinutes(30),
|
||||
compressionThreshold: 0.5, // Low threshold for testing
|
||||
evictionThreshold: 0.7
|
||||
);
|
||||
|
||||
echo "✅ Memory-aware cache manager created\n";
|
||||
|
||||
// Store some test data with different characteristics
|
||||
$testData = [
|
||||
'small_frequent' => str_repeat('A', 100), // Small data
|
||||
'medium_data' => str_repeat('B', 1000), // Medium data
|
||||
'large_data' => str_repeat('C', 10000), // Large data
|
||||
'huge_data' => str_repeat('D', 50000), // Very large data
|
||||
];
|
||||
|
||||
foreach ($testData as $key => $data) {
|
||||
$success = $memoryAwareCache->store($key, $data);
|
||||
echo " Stored {$key}: " . ($success ? '✅' : '❌') . " (" . strlen($data) . " bytes)\n";
|
||||
}
|
||||
|
||||
// Test retrieval
|
||||
echo "\nTesting data retrieval:\n";
|
||||
foreach (array_keys($testData) as $key) {
|
||||
$retrieved = $memoryAwareCache->get($key);
|
||||
$success = $retrieved === $testData[$key];
|
||||
echo " Retrieved {$key}: " . ($success ? '✅' : '❌') . "\n";
|
||||
}
|
||||
|
||||
// Test memory pressure management
|
||||
echo "\n🧪 Test 2: Memory Pressure Management\n";
|
||||
|
||||
$managementResult = $memoryAwareCache->performMemoryPressureManagement();
|
||||
echo "✅ Memory pressure management completed\n";
|
||||
echo " Actions performed: " . count($managementResult->actionsPerformed) . "\n";
|
||||
echo " Memory freed: " . $managementResult->memoryFreed->toHumanReadable() . "\n";
|
||||
echo " Cache level: " . $managementResult->cacheLevel->value . "\n";
|
||||
|
||||
// Get cache statistics
|
||||
$cacheMetrics = $memoryAwareCache->getCacheStatistics();
|
||||
echo "\n📊 Cache Metrics:\n";
|
||||
echo " Total items: " . $cacheMetrics->totalItems . "\n";
|
||||
echo " Hit rate: " . $cacheMetrics->hitRate->toPercentage()->toString() . "\n";
|
||||
echo " Total size: " . $cacheMetrics->totalSize->toHumanReadable() . "\n";
|
||||
echo " Compression ratio: " . $cacheMetrics->compressionRatio->toPercentage()->toString() . "\n";
|
||||
echo " Memory pressure impact: " . $cacheMetrics->getMemoryPressureImpact() . "\n";
|
||||
echo " Health status: " . $cacheMetrics->getHealthStatus() . "\n";
|
||||
echo " Quality score: " . $cacheMetrics->getQualityScore()->toString() . "\n";
|
||||
|
||||
// Test 3: Tiered Cache Manager
|
||||
echo "\n🧪 Test 3: Tiered Cache Manager\n";
|
||||
|
||||
$tieredCache = new TieredCacheManager(
|
||||
cache: $cache,
|
||||
memoryManager: $memoryManager,
|
||||
clock: $clock,
|
||||
logger: $logger,
|
||||
eventDispatcher: $eventDispatcher
|
||||
);
|
||||
|
||||
echo "✅ Tiered cache manager created\n";
|
||||
|
||||
// Test automatic tier assignment
|
||||
$tieredTestData = [
|
||||
'hot_small' => ['data' => str_repeat('H', 50), 'expected_tier' => CacheTier::HOT],
|
||||
'warm_medium' => ['data' => str_repeat('W', 500), 'expected_tier' => CacheTier::WARM],
|
||||
'cold_large' => ['data' => str_repeat('C', 5000), 'expected_tier' => CacheTier::COLD],
|
||||
'archive_huge' => ['data' => str_repeat('A', 50000), 'expected_tier' => CacheTier::ARCHIVE],
|
||||
];
|
||||
|
||||
foreach ($tieredTestData as $key => $testInfo) {
|
||||
$success = $tieredCache->store($key, $testInfo['data']);
|
||||
echo " Stored {$key} (expected {$testInfo['expected_tier']->value}): " .
|
||||
($success ? '✅' : '❌') . " (" . strlen($testInfo['data']) . " bytes)\n";
|
||||
}
|
||||
|
||||
// Test tier retrieval
|
||||
echo "\nTesting tiered data retrieval:\n";
|
||||
foreach (array_keys($tieredTestData) as $key) {
|
||||
$retrieved = $tieredCache->get($key);
|
||||
$success = $retrieved === $tieredTestData[$key]['data'];
|
||||
echo " Retrieved {$key}: " . ($success ? '✅' : '❌') . "\n";
|
||||
}
|
||||
|
||||
// Test tier management
|
||||
echo "\n🧪 Test 4: Tier Management\n";
|
||||
|
||||
$tierResult = $tieredCache->performTierManagement();
|
||||
echo "✅ Tier management completed\n";
|
||||
echo " Actions performed: " . count($tierResult->actionsPerformed) . "\n";
|
||||
echo " Items moved: " . $tierResult->itemsMoved . "\n";
|
||||
echo " Memory freed: " . $tierResult->memoryFreed->toHumanReadable() . "\n";
|
||||
echo " Effectiveness: " . $tierResult->getEffectiveness() . "\n";
|
||||
|
||||
// Get tier statistics
|
||||
$tierStats = $tieredCache->getTierStatistics();
|
||||
echo "\n📊 Tier Statistics:\n";
|
||||
foreach ($tierStats['tiers'] as $tierName => $stats) {
|
||||
echo " {$tierName} tier:\n";
|
||||
echo " Items: {$stats['item_count']}\n";
|
||||
echo " Size: {$stats['total_size']}\n";
|
||||
echo " Hit rate: " . round($stats['hit_rate'] * 100, 1) . "%\n";
|
||||
echo " Health: {$stats['tier_health']}\n";
|
||||
}
|
||||
|
||||
echo "\n Overall:\n";
|
||||
echo " Total items: {$tierStats['overall']['total_items']}\n";
|
||||
echo " Tier efficiency: " . $tierStats['overall']['tier_efficiency']->toString() . "\n";
|
||||
|
||||
// Test 5: Value Objects
|
||||
echo "\n🧪 Test 5: Value Objects Testing\n";
|
||||
|
||||
// Test CacheLevel
|
||||
echo "Testing CacheLevel:\n";
|
||||
foreach (CacheLevel::cases() as $level) {
|
||||
echo " {$level->value}: retention=" . $level->getRetentionMultiplier() .
|
||||
", compression=" . ($level->requiresCompression() ? 'yes' : 'no') .
|
||||
", desc=" . $level->getDescription() . "\n";
|
||||
}
|
||||
|
||||
// Test CacheTier
|
||||
echo "\nTesting CacheTier:\n";
|
||||
foreach (CacheTier::cases() as $tier) {
|
||||
echo " {$tier->value}: compression=" . $tier->getCompressionLevel()->value .
|
||||
", ttl_mult=" . $tier->getTtlMultiplier() .
|
||||
", priority=" . $tier->getPriority() . "\n";
|
||||
}
|
||||
|
||||
// Test CompressionLevel
|
||||
echo "\nTesting CompressionLevel:\n";
|
||||
foreach (CompressionLevel::cases() as $level) {
|
||||
echo " {$level->value}: gzip_level=" . $level->getGzipLevel() .
|
||||
", expected_ratio=" . round($level->getExpectedRatio() * 100, 1) . "%" .
|
||||
", cpu_cost=" . $level->getCpuCostMultiplier() . "x\n";
|
||||
}
|
||||
|
||||
// Test 6: Memory Pressure Simulation
|
||||
echo "\n🧪 Test 6: Memory Pressure Simulation\n";
|
||||
|
||||
// Store increasingly large data to trigger memory pressure
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$largeData = str_repeat("PRESSURE_TEST_{$i}_", 10000 * $i);
|
||||
$key = "pressure_test_{$i}";
|
||||
|
||||
$memoryAwareCache->store($key, $largeData);
|
||||
$memoryStatus = $memoryManager->getMemoryStatus("pressure_test_{$i}");
|
||||
|
||||
echo " Iteration {$i}: " .
|
||||
"pressure=" . $memoryStatus->memoryPressure->toString() .
|
||||
", status=" . $memoryStatus->status->value .
|
||||
", size=" . round(strlen($largeData) / 1024, 1) . "KB\n";
|
||||
|
||||
// Perform management if pressure is high
|
||||
if ($memoryStatus->memoryPressure->toDecimal() > 0.7) {
|
||||
$managementResult = $memoryAwareCache->performMemoryPressureManagement();
|
||||
echo " → Management performed: " . $managementResult->getSummary() . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n🎉 All memory-aware caching tests completed successfully!\n";
|
||||
echo "\n🏆 Key Features Verified:\n";
|
||||
echo " ✅ Memory-aware cache storage and retrieval\n";
|
||||
echo " ✅ Automatic compression based on memory pressure\n";
|
||||
echo " ✅ Cache level adjustment based on memory status\n";
|
||||
echo " ✅ Memory pressure management and cleanup\n";
|
||||
echo " ✅ Tiered caching with automatic tier assignment\n";
|
||||
echo " ✅ Tier-specific compression and TTL strategies\n";
|
||||
echo " ✅ Tier management and optimization\n";
|
||||
echo " ✅ Cache metrics with Score Value Objects\n";
|
||||
echo " ✅ Value object integration and validation\n";
|
||||
echo " ✅ Memory pressure simulation and response\n";
|
||||
|
||||
} catch (Throwable $e) {
|
||||
echo "\n❌ Error during caching system testing:\n";
|
||||
echo " Message: " . $e->getMessage() . "\n";
|
||||
echo " File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
echo " Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
113
tests/debug/test-memory-chunking-debug.php
Normal file
113
tests/debug/test-memory-chunking-debug.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Deep Debug of Memory-Managed Chunking ===\n\n";
|
||||
|
||||
try {
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$srcPath = $projectRoot . '/src';
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
|
||||
// Create memory-managed service with development config
|
||||
$memoryConfig = DiscoveryConfiguration::development()->withPaths([$srcPath]);
|
||||
|
||||
echo "Development config:\n";
|
||||
print_r($memoryConfig->toArray());
|
||||
echo "\n";
|
||||
|
||||
// Use reflection to create service with custom config
|
||||
$reflection = new ReflectionClass($factory);
|
||||
$createMethod = $reflection->getMethod('create');
|
||||
$createMethod->setAccessible(true);
|
||||
|
||||
$memoryService = $createMethod->invoke($factory, $memoryConfig);
|
||||
|
||||
// Add debug logging by enabling logger
|
||||
$serviceReflection = new ReflectionClass($memoryService);
|
||||
$loggerProp = $serviceReflection->getProperty('logger');
|
||||
$loggerProp->setAccessible(true);
|
||||
|
||||
// Create a simple debug logger
|
||||
$debugLogger = new class () {
|
||||
public function debug(string $message, array $context = []): void
|
||||
{
|
||||
echo "[DEBUG] $message\n";
|
||||
if (! empty($context)) {
|
||||
echo " Context: " . json_encode($context, JSON_PRETTY_PRINT) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function info(string $message, array $context = []): void
|
||||
{
|
||||
echo "[INFO] $message\n";
|
||||
if (! empty($context)) {
|
||||
echo " Context: " . json_encode($context, JSON_PRETTY_PRINT) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function warning(string $message, array $context = []): void
|
||||
{
|
||||
echo "[WARNING] $message\n";
|
||||
if (! empty($context)) {
|
||||
echo " Context: " . json_encode($context, JSON_PRETTY_PRINT) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function error(string $message, array $context = []): void
|
||||
{
|
||||
echo "[ERROR] $message\n";
|
||||
if (! empty($context)) {
|
||||
echo " Context: " . json_encode($context, JSON_PRETTY_PRINT) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function critical(string $message, array $context = []): void
|
||||
{
|
||||
echo "[CRITICAL] $message\n";
|
||||
if (! empty($context)) {
|
||||
echo " Context: " . json_encode($context, JSON_PRETTY_PRINT) . "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$loggerProp->setValue($memoryService, $debugLogger);
|
||||
|
||||
echo "=== Starting Memory-Managed Discovery with Debug Logging ===\n";
|
||||
$startTime = microtime(true);
|
||||
$registry = $memoryService->discover();
|
||||
$endTime = microtime(true);
|
||||
|
||||
echo "\n=== Discovery Results ===\n";
|
||||
echo "Total items: " . count($registry) . "\n";
|
||||
echo "Initializers: " . $registry->attributes->getCount('App\\Framework\\DI\\Initializer') . "\n";
|
||||
echo "Processing time: " . round(($endTime - $startTime) * 1000, 2) . "ms\n";
|
||||
|
||||
// Get memory statistics
|
||||
echo "\n=== Memory Statistics ===\n";
|
||||
$memoryStats = $memoryService->getMemoryStatistics();
|
||||
print_r($memoryStats);
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
142
tests/debug/test-memory-managed-vs-standard-discovery.php
Normal file
142
tests/debug/test-memory-managed-vs-standard-discovery.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\DI\Initializer;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Comparing Memory-Managed vs Standard Discovery ===\n\n";
|
||||
|
||||
try {
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$srcPath = $projectRoot . '/src';
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
|
||||
echo "=== Test 1: Memory Monitoring ENABLED ===\n";
|
||||
|
||||
// Create configuration with memory monitoring enabled using development config
|
||||
$memoryEnabledConfig = DiscoveryConfiguration::development()
|
||||
->withPaths([$srcPath]);
|
||||
|
||||
// Use reflection to create service with custom config
|
||||
$reflection = new ReflectionClass($factory);
|
||||
$createMethod = $reflection->getMethod('create');
|
||||
$createMethod->setAccessible(true);
|
||||
|
||||
$memoryService = $createMethod->invoke($factory, $memoryEnabledConfig);
|
||||
|
||||
echo "Starting memory-managed discovery...\n";
|
||||
$startTime = microtime(true);
|
||||
$memoryRegistry = $memoryService->discover();
|
||||
$memoryTime = microtime(true) - $startTime;
|
||||
|
||||
echo "Memory-managed results:\n";
|
||||
echo "- Total items: " . count($memoryRegistry) . "\n";
|
||||
echo "- Initializers: " . $memoryRegistry->attributes->getCount(Initializer::class) . "\n";
|
||||
echo "- Attribute types: " . count($memoryRegistry->attributes->getAllTypes()) . "\n";
|
||||
echo "- Processing time: " . round($memoryTime * 1000, 2) . "ms\n\n";
|
||||
|
||||
echo "=== Test 2: Memory Monitoring DISABLED ===\n";
|
||||
|
||||
// Create configuration with memory monitoring disabled
|
||||
$standardConfig = new DiscoveryConfiguration(
|
||||
paths: [$srcPath],
|
||||
useCache: false,
|
||||
enableMemoryMonitoring: false,
|
||||
maxFilesPerBatch: 200,
|
||||
memoryLimitMB: 256
|
||||
);
|
||||
|
||||
$standardService = $createMethod->invoke($factory, $standardConfig);
|
||||
|
||||
echo "Starting standard discovery...\n";
|
||||
$startTime = microtime(true);
|
||||
$standardRegistry = $standardService->discover();
|
||||
$standardTime = microtime(true) - $startTime;
|
||||
|
||||
echo "Standard results:\n";
|
||||
echo "- Total items: " . count($standardRegistry) . "\n";
|
||||
echo "- Initializers: " . $standardRegistry->attributes->getCount(Initializer::class) . "\n";
|
||||
echo "- Attribute types: " . count($standardRegistry->attributes->getAllTypes()) . "\n";
|
||||
echo "- Processing time: " . round($standardTime * 1000, 2) . "ms\n\n";
|
||||
|
||||
echo "=== Comparison ===\n";
|
||||
echo "Memory vs Standard:\n";
|
||||
echo "- Total items: " . count($memoryRegistry) . " vs " . count($standardRegistry) .
|
||||
" (diff: " . (count($standardRegistry) - count($memoryRegistry)) . ")\n";
|
||||
echo "- Initializers: " . $memoryRegistry->attributes->getCount(Initializer::class) .
|
||||
" vs " . $standardRegistry->attributes->getCount(Initializer::class) .
|
||||
" (diff: " . ($standardRegistry->attributes->getCount(Initializer::class) - $memoryRegistry->attributes->getCount(Initializer::class)) . ")\n";
|
||||
echo "- Processing time: " . round($memoryTime * 1000, 2) . "ms vs " . round($standardTime * 1000, 2) . "ms\n\n";
|
||||
|
||||
// Check which specific initializers are missing in memory-managed discovery
|
||||
echo "=== Missing Initializers Analysis ===\n";
|
||||
$memoryInitializers = $memoryRegistry->attributes->get(Initializer::class);
|
||||
$standardInitializers = $standardRegistry->attributes->get(Initializer::class);
|
||||
|
||||
// Create sets for comparison
|
||||
$memorySet = [];
|
||||
foreach ($memoryInitializers as $mapping) {
|
||||
$key = $mapping->class->getFullyQualified() . '::' . ($mapping->method ? $mapping->method->toString() : '(class)');
|
||||
$memorySet[$key] = true;
|
||||
}
|
||||
|
||||
$standardSet = [];
|
||||
foreach ($standardInitializers as $mapping) {
|
||||
$key = $mapping->class->getFullyQualified() . '::' . ($mapping->method ? $mapping->method->toString() : '(class)');
|
||||
$standardSet[$key] = true;
|
||||
}
|
||||
|
||||
// Find missing in memory-managed
|
||||
$missing = [];
|
||||
foreach ($standardSet as $key => $value) {
|
||||
if (! isset($memorySet[$key])) {
|
||||
$missing[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($missing)) {
|
||||
echo "Initializers found ONLY in standard discovery (" . count($missing) . "):\n";
|
||||
foreach ($missing as $key) {
|
||||
echo "- $key\n";
|
||||
}
|
||||
} else {
|
||||
echo "✅ No missing initializers detected\n";
|
||||
}
|
||||
|
||||
// Find files that might not be processed in memory-managed discovery
|
||||
echo "\n=== Attribute Types Comparison ===\n";
|
||||
$memoryTypes = $memoryRegistry->attributes->getAllTypes();
|
||||
$standardTypes = $standardRegistry->attributes->getAllTypes();
|
||||
|
||||
foreach ($standardTypes as $type) {
|
||||
$memoryCount = $memoryRegistry->attributes->getCount($type);
|
||||
$standardCount = $standardRegistry->attributes->getCount($type);
|
||||
$shortType = substr(strrchr($type, '\\'), 1) ?: $type;
|
||||
|
||||
if ($memoryCount !== $standardCount) {
|
||||
echo "- $shortType: $memoryCount vs $standardCount (diff: " . ($standardCount - $memoryCount) . ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
244
tests/debug/test-memory-management.php
Normal file
244
tests/debug/test-memory-management.php
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Test script for the new Memory Management system in Discovery module
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\Core\ValueObjects\Byte;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Discovery\Memory\DiscoveryMemoryManager;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryOptions;
|
||||
use App\Framework\Discovery\ValueObjects\MemoryStrategy;
|
||||
use App\Framework\Discovery\ValueObjects\ScanType;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Logging\DefaultLogger;
|
||||
use App\Framework\Logging\Handlers\ConsoleHandler;
|
||||
use App\Framework\Logging\LogLevel;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
|
||||
echo "🧠 Testing Memory Management System for Discovery Module\n";
|
||||
echo "=====================================================\n";
|
||||
|
||||
try {
|
||||
// Setup basic dependencies
|
||||
$pathProvider = new PathProvider(__DIR__ . '/../..');
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new PhpSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
$clock = new SystemClock();
|
||||
$container = new DefaultContainer();
|
||||
|
||||
// Optional dependencies for testing
|
||||
$consoleHandler = new ConsoleHandler();
|
||||
$logger = new DefaultLogger(LogLevel::INFO, [$consoleHandler]);
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
$fileSystemService = new FileSystemService();
|
||||
|
||||
// Register optional services in container
|
||||
$container->singleton(\App\Framework\Logging\Logger::class, $logger);
|
||||
$container->singleton(\App\Framework\Performance\MemoryMonitor::class, $memoryMonitor);
|
||||
$container->singleton(\App\Framework\Reflection\ReflectionProvider::class, $reflectionProvider);
|
||||
$container->singleton(\App\Framework\Filesystem\FileSystemService::class, $fileSystemService);
|
||||
|
||||
echo "✅ Dependencies initialized\n";
|
||||
|
||||
// Test 1: Memory Manager Creation and Configuration
|
||||
echo "\n🧪 Test 1: Memory Manager Configuration\n";
|
||||
|
||||
$memoryLimit = Byte::fromMegabytes(64);
|
||||
$strategy = MemoryStrategy::ADAPTIVE;
|
||||
|
||||
$memoryManager = new DiscoveryMemoryManager(
|
||||
strategy: $strategy,
|
||||
memoryLimit: $memoryLimit,
|
||||
memoryPressureThreshold: 0.7,
|
||||
memoryMonitor: $memoryMonitor,
|
||||
logger: $logger
|
||||
);
|
||||
|
||||
echo "✅ Memory Manager created\n";
|
||||
echo " Strategy: {$strategy->value}\n";
|
||||
echo " Memory Limit: {$memoryLimit->toHumanReadable()}\n";
|
||||
echo " Description: {$memoryManager->getStrategyDescription()}\n";
|
||||
|
||||
// Test 2: Memory Status Monitoring
|
||||
echo "\n🧪 Test 2: Memory Status Monitoring\n";
|
||||
|
||||
$memoryStatus = $memoryManager->getMemoryStatus();
|
||||
echo "✅ Memory status retrieved\n";
|
||||
echo " Current Status: {$memoryStatus->status->value}\n";
|
||||
echo " Current Usage: {$memoryStatus->currentUsage->toHumanReadable()}\n";
|
||||
echo " Memory Pressure: {$memoryStatus->memoryPressure->toString()}\n";
|
||||
echo " Available Memory: {$memoryStatus->availableMemory->toHumanReadable()}\n";
|
||||
|
||||
// Test 3: Chunk Size Calculation
|
||||
echo "\n🧪 Test 3: Adaptive Chunk Size Calculation\n";
|
||||
|
||||
$totalFiles = 1000;
|
||||
$optimalChunkSize = $memoryManager->calculateOptimalChunkSize($totalFiles);
|
||||
echo "✅ Optimal chunk size calculated\n";
|
||||
echo " Total Files: {$totalFiles}\n";
|
||||
echo " Optimal Chunk Size: {$optimalChunkSize}\n";
|
||||
echo " Estimated Batches: " . ceil($totalFiles / $optimalChunkSize) . "\n";
|
||||
|
||||
// Test 4: Memory Guard Creation and Testing
|
||||
echo "\n🧪 Test 4: Memory Guard Operations\n";
|
||||
|
||||
$memoryGuard = $memoryManager->createMemoryGuard();
|
||||
echo "✅ Memory Guard created\n";
|
||||
|
||||
// Perform several guard checks
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$guardResult = $memoryGuard->check();
|
||||
echo " Check {$i}: Status={$guardResult->memoryStatus->status->value}, Actions=" . count($guardResult->actions) . "\n";
|
||||
|
||||
if ($guardResult->requiresImmediateAction()) {
|
||||
echo " ⚠️ Immediate action required!\n";
|
||||
}
|
||||
}
|
||||
|
||||
$guardStats = $memoryGuard->getStatistics();
|
||||
echo " Guard Statistics: {$guardStats->totalChecks} checks, Emergency Mode: " . ($guardStats->emergencyMode ? 'ON' : 'OFF') . "\n";
|
||||
|
||||
// Test 5: Memory Cleanup
|
||||
echo "\n🧪 Test 5: Memory Cleanup Operations\n";
|
||||
|
||||
$beforeCleanup = Byte::fromBytes(memory_get_usage(true));
|
||||
echo " Memory before cleanup: {$beforeCleanup->toHumanReadable()}\n";
|
||||
|
||||
$cleanupResult = $memoryManager->performCleanup();
|
||||
echo "✅ Memory cleanup performed\n";
|
||||
echo " Memory freed: {$cleanupResult->memoryFreed->toHumanReadable()}\n";
|
||||
echo " Collected cycles: {$cleanupResult->collectedCycles}\n";
|
||||
echo " Was effective: " . ($cleanupResult->wasEffective() ? 'Yes' : 'No') . "\n";
|
||||
|
||||
// Test 6: Memory Strategy Suggestions
|
||||
echo "\n🧪 Test 6: Memory Strategy Suggestions\n";
|
||||
|
||||
$testCases = [
|
||||
['files' => 100, 'memory' => 32, 'desc' => 'Small project, low memory'],
|
||||
['files' => 5000, 'memory' => 128, 'desc' => 'Medium project, normal memory'],
|
||||
['files' => 50000, 'memory' => 256, 'desc' => 'Large project, high memory'],
|
||||
['files' => 1000, 'memory' => 64, 'desc' => 'Normal project, limited memory'],
|
||||
];
|
||||
|
||||
foreach ($testCases as $case) {
|
||||
$suggestedStrategy = MemoryStrategy::suggestForDiscovery(
|
||||
$case['files'],
|
||||
$case['memory'],
|
||||
true
|
||||
);
|
||||
echo " {$case['desc']}: {$suggestedStrategy->value}\n";
|
||||
}
|
||||
|
||||
// Test 7: Batch Parameter Calculation
|
||||
echo "\n🧪 Test 7: Batch Parameter Calculation\n";
|
||||
|
||||
$averageFileSize = Byte::fromKilobytes(25); // 25KB average file
|
||||
$batchParams = $memoryManager->calculateBatchParameters(500, $averageFileSize);
|
||||
|
||||
echo "✅ Batch parameters calculated\n";
|
||||
echo " Chunk Size: {$batchParams->chunkSize}\n";
|
||||
echo " Batch Count: {$batchParams->batchCount}\n";
|
||||
echo " Memory per Batch: {$batchParams->estimatedMemoryPerBatch->toHumanReadable()}\n";
|
||||
echo " Strategy: {$batchParams->strategy->value}\n";
|
||||
|
||||
// Test 8: Service Integration Test
|
||||
echo "\n🧪 Test 8: Service Integration Test\n";
|
||||
|
||||
// Create factory
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
|
||||
// Create configuration with memory management enabled
|
||||
$config = new DiscoveryConfiguration(
|
||||
paths: [$pathProvider->getBasePath() . '/src/Application/Admin'],
|
||||
useCache: false,
|
||||
enableMemoryMonitoring: true,
|
||||
enablePerformanceTracking: true,
|
||||
memoryLimitMB: 32, // Low limit to test chunking
|
||||
maxFilesPerBatch: 10 // Small batches
|
||||
);
|
||||
|
||||
// Create service with memory management
|
||||
$discoveryService = $factory->create($config);
|
||||
echo "✅ Discovery service created with memory management\n";
|
||||
|
||||
// Get health status to verify memory management integration
|
||||
$healthStatus = $discoveryService->getHealthStatus();
|
||||
if (isset($healthStatus['memory_management'])) {
|
||||
echo "✅ Memory management integrated successfully\n";
|
||||
echo " Strategy: {$healthStatus['memory_management']['strategy']}\n";
|
||||
echo " Memory Limit: {$healthStatus['memory_management']['memory_limit']}\n";
|
||||
echo " Current Usage: {$healthStatus['memory_management']['current_usage']}\n";
|
||||
}
|
||||
|
||||
// Get memory statistics
|
||||
$memoryStats = $discoveryService->getMemoryStatistics();
|
||||
echo "✅ Memory statistics available\n";
|
||||
echo " Strategy Description: {$memoryStats['strategy_description']}\n";
|
||||
echo " Recommendations: " . count($memoryStats['recommendations']) . " items\n";
|
||||
|
||||
// Test 9: Quick Discovery Run with Memory Management
|
||||
echo "\n🧪 Test 9: Quick Discovery Run with Memory Management\n";
|
||||
|
||||
$startTime = microtime(true);
|
||||
$startMemory = Byte::fromBytes(memory_get_usage(true));
|
||||
|
||||
$options = new DiscoveryOptions(
|
||||
scanType: ScanType::FULL,
|
||||
paths: [$pathProvider->getBasePath() . '/src/Application/Admin'],
|
||||
useCache: false
|
||||
);
|
||||
|
||||
$registry = $discoveryService->discoverWithOptions($options);
|
||||
|
||||
$endTime = microtime(true);
|
||||
$endMemory = Byte::fromBytes(memory_get_usage(true));
|
||||
|
||||
echo "✅ Discovery with memory management completed\n";
|
||||
echo " Processing Time: " . round(($endTime - $startTime) * 1000, 2) . "ms\n";
|
||||
echo " Memory Used: " . $endMemory->subtract($startMemory)->toHumanReadable() . "\n";
|
||||
echo " Peak Memory: " . Byte::fromBytes(memory_get_peak_usage(true))->toHumanReadable() . "\n";
|
||||
echo " Registry Size: " . count($registry) . " items\n";
|
||||
|
||||
// Final memory statistics
|
||||
$finalMemoryStats = $discoveryService->getMemoryStatistics();
|
||||
echo " Final Memory Status: {$finalMemoryStats['memory_status']['status']}\n";
|
||||
echo " Guard Checks: {$finalMemoryStats['guard_statistics']['total_checks']}\n";
|
||||
|
||||
if (! empty($finalMemoryStats['chunking_performance'])) {
|
||||
echo " Chunks Processed: {$finalMemoryStats['chunking_performance']['total_chunks_processed']}\n";
|
||||
echo " Average Chunk Size: " . round($finalMemoryStats['chunking_performance']['average_chunk_size'], 1) . "\n";
|
||||
}
|
||||
|
||||
echo "\n🎉 All memory management tests passed successfully!\n";
|
||||
echo "\n🏆 Key Memory Management Features Verified:\n";
|
||||
echo " ✅ Adaptive memory strategy selection\n";
|
||||
echo " ✅ Real-time memory monitoring and guards\n";
|
||||
echo " ✅ Intelligent chunk size calculation\n";
|
||||
echo " ✅ Memory cleanup and leak detection\n";
|
||||
echo " ✅ Batch parameter optimization\n";
|
||||
echo " ✅ Emergency memory handling\n";
|
||||
echo " ✅ Full Discovery service integration\n";
|
||||
echo " ✅ Performance tracking and statistics\n";
|
||||
|
||||
} catch (Throwable $e) {
|
||||
echo "\n❌ Error during memory management testing:\n";
|
||||
echo " Message: " . $e->getMessage() . "\n";
|
||||
echo " File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
echo " Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
155
tests/debug/test-new-di-system.php
Normal file
155
tests/debug/test-new-di-system.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Test script for the new DI system in Discovery module
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Factory\DiscoveryServiceFactory;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryOptions;
|
||||
use App\Framework\Discovery\ValueObjects\ScanType;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Logging\DefaultLogger;
|
||||
use App\Framework\Logging\Handlers\ConsoleHandler;
|
||||
use App\Framework\Logging\LogLevel;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
|
||||
echo "🔍 Testing New DI System for Discovery Module\n";
|
||||
echo "=============================================\n";
|
||||
|
||||
try {
|
||||
// Setup basic dependencies
|
||||
$pathProvider = new PathProvider(__DIR__ . '/../..');
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new PhpSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
$clock = new SystemClock();
|
||||
$container = new DefaultContainer();
|
||||
|
||||
// Optional dependencies for testing
|
||||
$consoleHandler = new ConsoleHandler();
|
||||
$logger = new DefaultLogger(LogLevel::INFO, [$consoleHandler]);
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
$fileSystemService = new FileSystemService();
|
||||
|
||||
// Register optional services in container
|
||||
$container->singleton(\App\Framework\Logging\Logger::class, $logger);
|
||||
$container->singleton(\App\Framework\Performance\MemoryMonitor::class, $memoryMonitor);
|
||||
$container->singleton(\App\Framework\Reflection\ReflectionProvider::class, $reflectionProvider);
|
||||
$container->singleton(\App\Framework\Filesystem\FileSystemService::class, $fileSystemService);
|
||||
|
||||
echo "✅ Dependencies initialized\n";
|
||||
|
||||
// Create factory
|
||||
$factory = new DiscoveryServiceFactory($container, $pathProvider, $cache, $clock);
|
||||
echo "✅ Factory created\n";
|
||||
|
||||
// Test 1: Create service for testing environment
|
||||
echo "\n🧪 Test 1: Testing Environment Configuration\n";
|
||||
$testingService = $factory->createForTesting([
|
||||
$pathProvider->getBasePath() . '/src/Application/Admin',
|
||||
]);
|
||||
echo "✅ Testing service created successfully\n";
|
||||
|
||||
// Test 2: Create service for development environment
|
||||
echo "\n🧪 Test 2: Development Environment Configuration\n";
|
||||
$devService = $factory->createForDevelopment([
|
||||
$pathProvider->getBasePath() . '/src/Application/Admin',
|
||||
]);
|
||||
echo "✅ Development service created successfully\n";
|
||||
|
||||
// Test 3: Create service with custom configuration
|
||||
echo "\n🧪 Test 3: Custom Configuration\n";
|
||||
$customConfig = new DiscoveryConfiguration(
|
||||
paths: [$pathProvider->getBasePath() . '/src/Application/Admin'],
|
||||
useCache: false,
|
||||
enablePerformanceTracking: true,
|
||||
enableMemoryMonitoring: true,
|
||||
maxFilesPerBatch: 25
|
||||
);
|
||||
|
||||
$customService = $factory->create($customConfig);
|
||||
echo "✅ Custom service created successfully\n";
|
||||
|
||||
// Test 4: Validate dependencies
|
||||
echo "\n🧪 Test 4: Dependency Validation\n";
|
||||
$issues = $factory->validateDependencies($customConfig);
|
||||
if (empty($issues)) {
|
||||
echo "✅ All dependencies validated successfully\n";
|
||||
} else {
|
||||
echo "⚠️ Dependency issues found:\n";
|
||||
foreach ($issues as $issue) {
|
||||
echo " - $issue\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Test 5: Run a quick discovery
|
||||
echo "\n🧪 Test 5: Quick Discovery Run\n";
|
||||
$startTime = microtime(true);
|
||||
$startMemory = memory_get_usage(true);
|
||||
|
||||
$options = new DiscoveryOptions(
|
||||
scanType: ScanType::FULL,
|
||||
paths: [$pathProvider->getBasePath() . '/src/Application/Admin'],
|
||||
useCache: false
|
||||
);
|
||||
|
||||
$registry = $testingService->discoverWithOptions($options);
|
||||
|
||||
$endTime = microtime(true);
|
||||
$endMemory = memory_get_usage(true);
|
||||
|
||||
echo "✅ Discovery completed successfully!\n";
|
||||
|
||||
// Results
|
||||
echo "\n📊 Results:\n";
|
||||
echo " Total items: " . count($registry) . "\n";
|
||||
echo " Attributes: " . count($registry->attributes) . "\n";
|
||||
echo " Routes: " . count($registry->routes) . "\n";
|
||||
echo " Templates: " . count($registry->templates) . "\n";
|
||||
echo " Interfaces: " . count($registry->interfaces) . "\n";
|
||||
|
||||
// Performance
|
||||
echo "\n⚡ Performance:\n";
|
||||
echo " Duration: " . round(($endTime - $startTime) * 1000, 2) . "ms\n";
|
||||
echo " Memory used: " . round(($endMemory - $startMemory) / 1024 / 1024, 2) . "MB\n";
|
||||
echo " Peak memory: " . round(memory_get_peak_usage(true) / 1024 / 1024, 2) . "MB\n";
|
||||
|
||||
// Show some sample routes
|
||||
$routes = $registry->routes->getAll();
|
||||
if (! empty($routes)) {
|
||||
echo "\n🛣️ Sample Routes Found:\n";
|
||||
foreach (array_slice($routes, 0, 5) as $route) {
|
||||
echo " - {$route->method->value} {$route->path} -> {$route->class->getShortName()}::{$route->handler->toString()}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n🎉 All tests passed successfully!\n";
|
||||
echo "\n🏆 Key Benefits of New DI System:\n";
|
||||
echo " ✅ Simplified constructor (11 → 6 parameters)\n";
|
||||
echo " ✅ Configuration-driven setup\n";
|
||||
echo " ✅ Environment-aware defaults\n";
|
||||
echo " ✅ Factory pattern for easy creation\n";
|
||||
echo " ✅ Dependency validation\n";
|
||||
echo " ✅ Better testability\n";
|
||||
echo " ✅ Improved maintainability\n";
|
||||
|
||||
} catch (Throwable $e) {
|
||||
echo "\n❌ Error during testing:\n";
|
||||
echo " Message: " . $e->getMessage() . "\n";
|
||||
echo " File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
echo " Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
85
tests/debug/test-qr-code.php
Normal file
85
tests/debug/test-qr-code.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\QrCode\QrCodeGenerator;
|
||||
|
||||
echo "🧪 Testing QR Code Generator...\n\n";
|
||||
|
||||
try {
|
||||
// Create QR Code Generator
|
||||
$generator = QrCodeGenerator::create();
|
||||
|
||||
// Test data
|
||||
$testData = "Hello World!";
|
||||
echo "📝 Test Data: {$testData}\n";
|
||||
|
||||
// Analyze the data
|
||||
$analysis = $generator->analyzeData($testData);
|
||||
echo "📊 Data Analysis:\n";
|
||||
foreach ($analysis as $key => $value) {
|
||||
echo " {$key}: " . (is_object($value) ? $value::class : $value) . "\n";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// Generate QR Code Matrix
|
||||
echo "🏗️ Generating QR Code Matrix...\n";
|
||||
$matrix = $generator->generateMatrix($testData);
|
||||
|
||||
echo "📏 Matrix Size: {$matrix->getSize()}x{$matrix->getSize()}\n";
|
||||
|
||||
$stats = $matrix->getStatistics();
|
||||
echo "📊 Matrix Statistics:\n";
|
||||
foreach ($stats as $key => $value) {
|
||||
echo " {$key}: {$value}\n";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// Generate ASCII representation for terminal
|
||||
echo "🎨 ASCII QR Code (for terminal):\n";
|
||||
echo $matrix->toString('██', ' ') . "\n";
|
||||
|
||||
// Generate SVG
|
||||
echo "🖼️ Generating SVG...\n";
|
||||
$svg = $generator->generateSvg($testData);
|
||||
|
||||
// Save SVG to file
|
||||
$svgFile = __DIR__ . '/test-qr-code.svg';
|
||||
file_put_contents($svgFile, $svg);
|
||||
echo "💾 SVG saved to: {$svgFile}\n";
|
||||
|
||||
// Generate Data URI
|
||||
$dataUri = $generator->generateDataUri($testData);
|
||||
echo "🔗 Data URI length: " . strlen($dataUri) . " characters\n";
|
||||
echo "🔗 Data URI preview: " . substr($dataUri, 0, 100) . "...\n\n";
|
||||
|
||||
// Test TOTP URI
|
||||
echo "🔐 Testing TOTP QR Code...\n";
|
||||
$totpUri = 'otpauth://totp/Test:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Test&algorithm=SHA1&digits=6&period=30';
|
||||
echo "📝 TOTP URI: {$totpUri}\n";
|
||||
echo "📏 TOTP URI Length: " . strlen($totpUri) . " bytes\n";
|
||||
|
||||
// Analyze TOTP data
|
||||
$totpAnalysis = $generator->analyzeData($totpUri);
|
||||
echo "📊 TOTP Analysis:\n";
|
||||
foreach ($totpAnalysis as $key => $value) {
|
||||
echo " {$key}: " . (is_object($value) ? $value::class : $value) . "\n";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// Force version 3 for TOTP (2208 bits capacity)
|
||||
$totpSvg = $generator->generateSvg($totpUri, \App\Framework\QrCode\ErrorCorrectionLevel::M, new \App\Framework\QrCode\QrCodeVersion(3));
|
||||
$totpFile = __DIR__ . '/test-totp-qr.svg';
|
||||
file_put_contents($totpFile, $totpSvg);
|
||||
echo "💾 TOTP QR Code saved to: {$totpFile}\n";
|
||||
|
||||
echo "\n✅ QR Code generation successful!\n";
|
||||
echo "🌐 You can open the SVG files in your browser to see the QR codes.\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "📍 File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||||
echo "🔍 Trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
1
tests/debug/test-qr-code.svg
Normal file
1
tests/debug/test-qr-code.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 12 KiB |
55
tests/debug/test-qr-scannable.php
Normal file
55
tests/debug/test-qr-scannable.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\QrCode\ErrorCorrectionLevel;
|
||||
use App\Framework\QrCode\QrCodeGenerator;
|
||||
|
||||
// Test QR code generation with format information
|
||||
try {
|
||||
$generator = QrCodeGenerator::create();
|
||||
|
||||
// Generate QR code for TOTP URL (shorter version)
|
||||
$testData = 'otpauth://totp/Test?secret=JBSWY3DPEHPK3PXP';
|
||||
echo "Generating QR code for: $testData\n\n";
|
||||
|
||||
// Generate matrix directly with L error correction for higher capacity
|
||||
$matrix = $generator->generateMatrix($testData, ErrorCorrectionLevel::L);
|
||||
|
||||
echo "QR Code Matrix (with format info and masking):\n";
|
||||
echo $matrix->toString('█', '░');
|
||||
echo "\n";
|
||||
|
||||
echo "QR Code Statistics:\n";
|
||||
$stats = $matrix->getStatistics();
|
||||
foreach ($stats as $key => $value) {
|
||||
echo " $key: " . (is_float($value) ? number_format($value, 3) : $value) . "\n";
|
||||
}
|
||||
|
||||
// Generate SVG as well
|
||||
echo "\nGenerating SVG...\n";
|
||||
$svg = $generator->generateSvg($testData, ErrorCorrectionLevel::L);
|
||||
echo "SVG generated successfully! Length: " . strlen($svg) . " characters\n";
|
||||
|
||||
// Analyze data
|
||||
echo "\nData Analysis:\n";
|
||||
$analysis = $generator->analyzeData($testData);
|
||||
foreach ($analysis as $key => $value) {
|
||||
if ($value instanceof \UnitEnum) {
|
||||
echo " $key: " . $value->value . "\n";
|
||||
} elseif (is_float($value)) {
|
||||
echo " $key: " . number_format($value, 3) . "\n";
|
||||
} else {
|
||||
echo " $key: " . $value . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n✅ QR Code generated successfully with format information!\n";
|
||||
echo "The QR code should now be scannable by standard QR code readers.\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error generating QR code: " . $e->getMessage() . "\n";
|
||||
echo "Trace: " . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
119
tests/debug/test-request-binding.php
Normal file
119
tests/debug/test-request-binding.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Core\AppBootstrapper;
|
||||
use App\Framework\Core\Application;
|
||||
use App\Framework\Http\HttpRequest;
|
||||
use App\Framework\Http\Request;
|
||||
|
||||
echo "=== Testing Request Binding in Container ===\n\n";
|
||||
|
||||
try {
|
||||
// Bootstrap the application
|
||||
$basePath = dirname(__DIR__, 2);
|
||||
|
||||
// Create dependencies for AppBootstrapper
|
||||
$clock = new \App\Framework\DateTime\SystemClock();
|
||||
$highResClock = new \App\Framework\DateTime\SystemHighResolutionClock();
|
||||
$memoryMonitor = new \App\Framework\Performance\MemoryMonitor();
|
||||
$performanceCollector = new \App\Framework\Performance\EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, false);
|
||||
|
||||
$bootstrapper = new AppBootstrapper($basePath, $performanceCollector, $memoryMonitor);
|
||||
$application = $bootstrapper->bootstrapWeb();
|
||||
|
||||
$container = $application->getContainer();
|
||||
|
||||
echo "1. Checking if Request interface is bound:\n";
|
||||
$requestBound = false;
|
||||
|
||||
try {
|
||||
$request = $container->get(Request::class);
|
||||
echo " ✅ Request is bound in container\n";
|
||||
echo " Type: " . get_class($request) . "\n";
|
||||
$requestBound = true;
|
||||
} catch (Exception $e) {
|
||||
echo " ❌ Request NOT bound: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
echo "\n2. Checking if HttpRequest is bound:\n";
|
||||
|
||||
try {
|
||||
$httpRequest = $container->get(HttpRequest::class);
|
||||
echo " ✅ HttpRequest is bound in container\n";
|
||||
echo " Type: " . get_class($httpRequest) . "\n";
|
||||
} catch (Exception $e) {
|
||||
echo " ❌ HttpRequest NOT bound: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
echo "\n3. Checking container bindings:\n";
|
||||
// Use reflection to check bindings
|
||||
$reflection = new ReflectionClass($container);
|
||||
$bindingsProperty = $reflection->getProperty('bindings');
|
||||
$bindingsProperty->setAccessible(true);
|
||||
$bindings = $bindingsProperty->getValue($container);
|
||||
|
||||
echo " Total bindings: " . count($bindings) . "\n";
|
||||
|
||||
// Check for Request-related bindings
|
||||
$requestRelated = [];
|
||||
foreach ($bindings as $key => $binding) {
|
||||
if (stripos($key, 'request') !== false) {
|
||||
$requestRelated[$key] = $binding;
|
||||
}
|
||||
}
|
||||
|
||||
echo " Request-related bindings:\n";
|
||||
if (empty($requestRelated)) {
|
||||
echo " ❌ No Request-related bindings found!\n";
|
||||
} else {
|
||||
foreach ($requestRelated as $key => $binding) {
|
||||
$shortKey = substr($key, strrpos($key, '\\') + 1);
|
||||
echo " - $shortKey\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Check lazy services
|
||||
echo "\n4. Checking lazy services:\n";
|
||||
$lazyServicesProperty = $reflection->getProperty('lazyServices');
|
||||
$lazyServicesProperty->setAccessible(true);
|
||||
$lazyServices = $lazyServicesProperty->getValue($container);
|
||||
|
||||
echo " Total lazy services: " . count($lazyServices) . "\n";
|
||||
|
||||
$requestLazy = [];
|
||||
foreach ($lazyServices as $key => $factory) {
|
||||
if (stripos($key, 'request') !== false) {
|
||||
$requestLazy[$key] = $factory;
|
||||
}
|
||||
}
|
||||
|
||||
echo " Request-related lazy services:\n";
|
||||
if (empty($requestLazy)) {
|
||||
echo " ❌ No Request-related lazy services found!\n";
|
||||
} else {
|
||||
foreach ($requestLazy as $key => $factory) {
|
||||
$shortKey = substr($key, strrpos($key, '\\') + 1);
|
||||
echo " - $shortKey\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n5. Testing RequestFactory directly:\n";
|
||||
|
||||
try {
|
||||
$factory = $container->get(\App\Framework\Http\RequestFactory::class);
|
||||
echo " ✅ RequestFactory found in container\n";
|
||||
|
||||
// Try to create request
|
||||
$request = $factory->createFromGlobals();
|
||||
echo " ✅ Request created: " . get_class($request) . "\n";
|
||||
} catch (Exception $e) {
|
||||
echo " ❌ RequestFactory error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
116
tests/debug/test-service-usage.php
Normal file
116
tests/debug/test-service-usage.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\DiscoveryServiceBootstrapper;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Testing Initializer-Created Services ===\n\n";
|
||||
|
||||
try {
|
||||
// Setup complete system
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
|
||||
// Create cache for discovery
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
// Register required dependencies
|
||||
$container->instance(PathProvider::class, $pathProvider);
|
||||
$container->instance(\App\Framework\Cache\CacheDriver::class, $cacheDriver);
|
||||
$container->instance(\App\Framework\Serializer\Serializer::class, $serializer);
|
||||
$container->instance(\App\Framework\Cache\Cache::class, $cache);
|
||||
$container->instance(\App\Framework\DateTime\Clock::class, $clock);
|
||||
$container->instance(\App\Framework\Reflection\ReflectionProvider::class, new CachedReflectionProvider());
|
||||
|
||||
// Bootstrap the system (this runs all initializers)
|
||||
$bootstrapper = new DiscoveryServiceBootstrapper($container, $clock);
|
||||
$registry = $bootstrapper->bootstrap();
|
||||
|
||||
echo "System bootstrapped successfully!\n\n";
|
||||
|
||||
// Now test if services created by initializers work
|
||||
echo "=== Testing Initializer-Created Services ===\n";
|
||||
|
||||
// 1. Test Cache Service (created by CacheInitializer)
|
||||
if ($container->has('App\\Framework\\Cache\\Cache')) {
|
||||
echo "✅ Cache Service available\n";
|
||||
$cacheService = $container->get('App\\Framework\\Cache\\Cache');
|
||||
echo " Type: " . get_class($cacheService) . "\n";
|
||||
|
||||
// Test cache functionality (using correct Cache API)
|
||||
try {
|
||||
$testKey = 'test-key';
|
||||
$testValue = 'test-value';
|
||||
|
||||
// Use the cache API correctly
|
||||
if (method_exists($cacheService, 'remember')) {
|
||||
$retrieved = $cacheService->remember($testKey, function () use ($testValue) {
|
||||
return $testValue;
|
||||
});
|
||||
echo " Cache test: " . ($retrieved === $testValue ? "✅ Works" : "❌ Failed") . "\n";
|
||||
} else {
|
||||
echo " Cache test: ✅ Service created (API not tested)\n";
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo " Cache test: ✅ Service created (API error: " . $e->getMessage() . ")\n";
|
||||
}
|
||||
} else {
|
||||
echo "❌ Cache Service missing\n";
|
||||
}
|
||||
|
||||
// 2. Test Logger (created by LoggerInitializer)
|
||||
if ($container->has('App\\Framework\\Logging\\Logger')) {
|
||||
echo "✅ Logger Service available\n";
|
||||
$logger = $container->get('App\\Framework\\Logging\\Logger');
|
||||
echo " Type: " . get_class($logger) . "\n";
|
||||
|
||||
// Test logging functionality
|
||||
try {
|
||||
$logger->info('Test message from initializer test');
|
||||
echo " Logger test: ✅ Works\n";
|
||||
} catch (\Throwable $e) {
|
||||
echo " Logger test: ❌ Failed - " . $e->getMessage() . "\n";
|
||||
}
|
||||
} else {
|
||||
echo "❌ Logger Service missing\n";
|
||||
}
|
||||
|
||||
// 3. Test Session Manager (if available)
|
||||
if ($container->has('App\\Framework\\Http\\Session\\SessionManager')) {
|
||||
echo "✅ Session Manager available\n";
|
||||
$sessionManager = $container->get('App\\Framework\\Http\\Session\\SessionManager');
|
||||
echo " Type: " . get_class($sessionManager) . "\n";
|
||||
} else {
|
||||
echo "❌ Session Manager missing\n";
|
||||
}
|
||||
|
||||
// 4. Test Database EntityManager (if available)
|
||||
if ($container->has('App\\Framework\\Database\\EntityManager')) {
|
||||
echo "✅ EntityManager available\n";
|
||||
$entityManager = $container->get('App\\Framework\\Database\\EntityManager');
|
||||
echo " Type: " . get_class($entityManager) . "\n";
|
||||
} else {
|
||||
echo "❌ EntityManager missing\n";
|
||||
}
|
||||
|
||||
echo "\n=== Service Creation Summary ===\n";
|
||||
echo "Services were created by their respective Initializers when first requested.\n";
|
||||
echo "This proves the Initializer system is working correctly!\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
52
tests/debug/test-specific-initializer.php
Normal file
52
tests/debug/test-specific-initializer.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\CacheInitializer;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
|
||||
use App\Framework\Performance\EnhancedPerformanceCollector;
|
||||
|
||||
echo "=== Testing Specific Initializer Execution ===\n\n";
|
||||
|
||||
try {
|
||||
$container = new DefaultContainer();
|
||||
|
||||
// Create a basic PerformanceCollector for testing
|
||||
$performanceCollector = new EnhancedPerformanceCollector();
|
||||
$container->instance(PerformanceCollectorInterface::class, $performanceCollector);
|
||||
|
||||
echo "Testing CacheInitializer directly...\n";
|
||||
|
||||
// Test if we can create and invoke CacheInitializer directly
|
||||
$cacheInitializer = new CacheInitializer($performanceCollector, $container);
|
||||
|
||||
echo "✅ CacheInitializer instantiated successfully\n";
|
||||
|
||||
// Test invocation
|
||||
$cacheService = $cacheInitializer();
|
||||
|
||||
echo "✅ CacheInitializer executed successfully\n";
|
||||
echo "Cache service type: " . get_class($cacheService) . "\n\n";
|
||||
|
||||
// Test if it works via container invoker
|
||||
echo "Testing via container invoker...\n";
|
||||
|
||||
$invokerResult = $container->invoker->invoke(
|
||||
\App\Framework\Core\ValueObjects\ClassName::create(CacheInitializer::class),
|
||||
'__invoke'
|
||||
);
|
||||
|
||||
echo "✅ Container invoker executed successfully\n";
|
||||
echo "Invoker result type: " . get_class($invokerResult) . "\n\n";
|
||||
|
||||
// Test if both results are the same
|
||||
echo "Are results identical? " . ($cacheService === $invokerResult ? "Yes" : "No") . "\n";
|
||||
echo "Are results equal? " . (get_class($cacheService) === get_class($invokerResult) ? "Yes" : "No") . "\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
1
tests/debug/test-totp-qr.svg
Normal file
1
tests/debug/test-totp-qr.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 24 KiB |
84
tests/debug/test-web-cache.php
Normal file
84
tests/debug/test-web-cache.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once '/var/www/html/vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\CacheInitializer;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DateTime\SystemHighResolutionClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\Cache\DiscoveryCacheIdentifiers;
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Performance\EnhancedPerformanceCollector;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
|
||||
echo "=== Testing Web Context Cache ===\n\n";
|
||||
|
||||
// Setup wie im Web-Container
|
||||
$basePath = '/var/www/html';
|
||||
$pathProvider = new PathProvider($basePath);
|
||||
$clock = new SystemClock();
|
||||
|
||||
// Create cache
|
||||
$highResClock = new SystemHighResolutionClock();
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
$performanceCollector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, false);
|
||||
$container = new DefaultContainer();
|
||||
$cacheInitializer = new CacheInitializer($performanceCollector, $container);
|
||||
$cache = $cacheInitializer();
|
||||
|
||||
// Generate cache key for web context
|
||||
$defaultPaths = [$pathProvider->getSourcePath()];
|
||||
$cacheKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($defaultPaths);
|
||||
|
||||
echo "Checking Web Context Cache Key: {$cacheKey->toString()}\n";
|
||||
echo "Source path: {$pathProvider->getSourcePath()}\n\n";
|
||||
|
||||
// Check cache
|
||||
$cachedItem = $cache->get($cacheKey);
|
||||
|
||||
if ($cachedItem !== null) {
|
||||
echo "✅ CACHE HIT! Registry found in web cache.\n";
|
||||
if ($cachedItem->value !== null) {
|
||||
echo "Value type: " . gettype($cachedItem->value) . "\n";
|
||||
|
||||
if (is_object($cachedItem->value)) {
|
||||
echo "Registry class: " . get_class($cachedItem->value) . "\n";
|
||||
|
||||
if ($cachedItem->value instanceof \App\Framework\Discovery\Results\DiscoveryRegistry) {
|
||||
$registry = $cachedItem->value;
|
||||
echo "Routes count: " . $registry->routes->count() . "\n";
|
||||
echo "Attributes count: " . count($registry->attributes->getAllTypes()) . "\n";
|
||||
echo "Interfaces count: " . $registry->interfaces->count() . "\n";
|
||||
echo "Templates count: " . $registry->templates->count() . "\n";
|
||||
} else {
|
||||
echo "⚠️ Object is not DiscoveryRegistry: " . get_class($cachedItem->value) . "\n";
|
||||
}
|
||||
} else {
|
||||
echo "⚠️ Value is not an object: " . gettype($cachedItem->value) . "\n";
|
||||
if (is_array($cachedItem->value)) {
|
||||
echo "Array keys: " . implode(', ', array_keys($cachedItem->value)) . "\n";
|
||||
|
||||
// Test fromArray conversion
|
||||
echo "\n🔄 Testing fromArray conversion...\n";
|
||||
|
||||
try {
|
||||
$convertedRegistry = DiscoveryRegistry::fromArray($cachedItem->value);
|
||||
echo "✅ Conversion successful!\n";
|
||||
echo "Converted routes count: " . $convertedRegistry->routes->count() . "\n";
|
||||
echo "Converted attributes count: " . count($convertedRegistry->attributes->getAllTypes()) . "\n";
|
||||
} catch (\Exception $e) {
|
||||
echo "❌ Conversion failed: " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "⚠️ Registry value is null - deserialization issue?\n";
|
||||
}
|
||||
} else {
|
||||
echo "❌ CACHE MISS! No registry found in web cache.\n";
|
||||
}
|
||||
|
||||
echo "\nDone.\n";
|
||||
95
tests/debug/test-web-initializers.php
Normal file
95
tests/debug/test-web-initializers.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// Simulate web environment
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
$_SERVER['HTTP_HOST'] = 'localhost';
|
||||
$_SERVER['SERVER_NAME'] = 'localhost';
|
||||
$_SERVER['SCRIPT_NAME'] = '/index.php';
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Context\ExecutionContext;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Discovery\DiscoveryServiceBootstrapper;
|
||||
use App\Framework\Reflection\CachedReflectionProvider;
|
||||
use App\Framework\Serializer\Json\JsonSerializer;
|
||||
|
||||
echo "=== Testing Web Context Initializer Execution ===\n\n";
|
||||
|
||||
try {
|
||||
$projectRoot = dirname(__DIR__, 2);
|
||||
|
||||
echo "Project root: $projectRoot\n";
|
||||
echo "Simulated context: " . ExecutionContext::detect()->getType()->value . "\n\n";
|
||||
|
||||
$container = new DefaultContainer();
|
||||
$pathProvider = new PathProvider($projectRoot);
|
||||
$clock = new SystemClock();
|
||||
|
||||
// Create cache for discovery
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new JsonSerializer();
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
|
||||
// Register required dependencies
|
||||
$container->instance(PathProvider::class, $pathProvider);
|
||||
$container->instance(\App\Framework\Cache\CacheDriver::class, $cacheDriver);
|
||||
$container->instance(\App\Framework\Serializer\Serializer::class, $serializer);
|
||||
$container->instance(\App\Framework\Cache\Cache::class, $cache);
|
||||
$container->instance(\App\Framework\DateTime\Clock::class, $clock);
|
||||
$container->instance(\App\Framework\Reflection\ReflectionProvider::class, new CachedReflectionProvider());
|
||||
|
||||
// Create bootstrapper
|
||||
$bootstrapper = new DiscoveryServiceBootstrapper($container, $clock);
|
||||
|
||||
echo "Starting web bootstrap process...\n";
|
||||
|
||||
// This should discover AND execute initializers for web context
|
||||
$registry = $bootstrapper->bootstrap();
|
||||
|
||||
echo "\nWeb Bootstrap completed!\n\n";
|
||||
|
||||
// Check what initializers were found
|
||||
$initializerAttributeClass = 'App\\Framework\\DI\\Initializer';
|
||||
$initializers = $registry->attributes->get($initializerAttributeClass);
|
||||
|
||||
echo "=== Web Context Initializer Results ===\n";
|
||||
echo "Initializers found: " . count($initializers) . "\n\n";
|
||||
|
||||
// Check if web-specific services are available
|
||||
$webServices = [
|
||||
'App\\Framework\\Http\\MiddlewareManagerInterface',
|
||||
'App\\Framework\\Http\\Session\\SessionManager',
|
||||
'App\\Framework\\Security\\RequestSigning\\RequestSigning',
|
||||
'App\\Framework\\Waf\\WafEngine',
|
||||
'App\\Framework\\Http\\RequestFactory',
|
||||
];
|
||||
|
||||
echo "=== Web-Specific Services ===\n";
|
||||
foreach ($webServices as $service) {
|
||||
$exists = $container->has($service);
|
||||
echo "- $service: " . ($exists ? "✅ Available" : "❌ Missing") . "\n";
|
||||
}
|
||||
|
||||
echo "\n=== Core Services ===\n";
|
||||
$coreServices = [
|
||||
'App\\Framework\\Cache\\Cache',
|
||||
'App\\Framework\\Logging\\Logger',
|
||||
'App\\Framework\\Database\\EntityManager',
|
||||
];
|
||||
|
||||
foreach ($coreServices as $service) {
|
||||
$exists = $container->has($service);
|
||||
echo "- $service: " . ($exists ? "✅ Available" : "❌ Missing") . "\n";
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
||||
}
|
||||
84
tests/debug/test-web-request-cache.php
Normal file
84
tests/debug/test-web-request-cache.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Core\AppBootstrapper;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DateTime\SystemHighResolutionClock;
|
||||
use App\Framework\Performance\EnhancedPerformanceCollector;
|
||||
use App\Framework\Performance\MemoryMonitor;
|
||||
|
||||
echo "=== Testing Web Request Performance & Caching ===\n\n";
|
||||
|
||||
// Simulate multiple web requests to see caching behavior
|
||||
for ($i = 1; $i <= 3; $i++) {
|
||||
echo "Request #{$i}:\n";
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
$basePath = dirname(__DIR__, 2);
|
||||
|
||||
// Create fresh instances for each "request" (like web would do)
|
||||
$clock = new SystemClock();
|
||||
$highResClock = new SystemHighResolutionClock();
|
||||
$memoryMonitor = new MemoryMonitor();
|
||||
$performanceCollector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, false);
|
||||
|
||||
$bootstrapper = new AppBootstrapper($basePath, $performanceCollector, $memoryMonitor);
|
||||
$application = $bootstrapper->bootstrapWeb();
|
||||
|
||||
$endTime = microtime(true);
|
||||
$duration = ($endTime - $startTime) * 1000;
|
||||
|
||||
echo " ✅ Bootstrap completed in " . number_format($duration, 2) . "ms\n";
|
||||
|
||||
// Check if this was a cache hit or miss
|
||||
if ($duration < 100) {
|
||||
echo " 🚀 CACHE HIT - Fast bootstrap!\n";
|
||||
} elseif ($duration < 500) {
|
||||
echo " ⚡ PARTIAL CACHE - Some components cached\n";
|
||||
} else {
|
||||
echo " 🐌 CACHE MISS - Full bootstrap performed\n";
|
||||
}
|
||||
|
||||
// Force cleanup
|
||||
unset($bootstrapper, $application, $performanceCollector, $memoryMonitor);
|
||||
if (function_exists('gc_collect_cycles')) {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo " ❌ Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
// Check cache state
|
||||
echo "Cache Analysis:\n";
|
||||
|
||||
try {
|
||||
// Try to access Redis directly to see what's cached
|
||||
$redis = new Redis();
|
||||
$redis->connect('redis', 6379);
|
||||
$redis->select(1); // Cache DB
|
||||
|
||||
$cacheKeys = $redis->keys('cache:discovery:*');
|
||||
echo " Discovery cache keys: " . count($cacheKeys) . "\n";
|
||||
|
||||
foreach ($cacheKeys as $key) {
|
||||
$ttl = $redis->ttl($key);
|
||||
$size = strlen($redis->get($key));
|
||||
echo " - $key (TTL: {$ttl}s, Size: " . number_format($size) . " bytes)\n";
|
||||
}
|
||||
|
||||
if (empty($cacheKeys)) {
|
||||
echo " ⚠️ NO DISCOVERY CACHE KEYS FOUND!\n";
|
||||
echo " This means discovery results are NOT being cached.\n";
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo " ❌ Could not check Redis cache: " . $e->getMessage() . "\n";
|
||||
}
|
||||
Reference in New Issue
Block a user