refactor: reorganize project structure for better maintainability

- Move 45 debug/test files from root to organized scripts/ directories
- Secure public/ directory by removing debug files (security improvement)
- Create structured scripts organization:
  • scripts/debug/      (20 files) - Framework debugging tools
  • scripts/test/       (18 files) - Test and validation scripts
  • scripts/maintenance/ (5 files) - Maintenance utilities
  • scripts/dev/         (2 files) - Development tools

Security improvements:
- Removed all debug/test files from public/ directory
- Only production files remain: index.php, health.php

Root directory cleanup:
- Reduced from 47 to 2 PHP files in root
- Only essential production files: console.php, worker.php

This improves:
 Security (no debug code in public/)
 Organization (clear separation of concerns)
 Maintainability (easy to find and manage scripts)
 Professional structure (clean root directory)
This commit is contained in:
2025-10-05 10:59:15 +02:00
parent 03e5188644
commit 887847dde6
77 changed files with 3902 additions and 787 deletions

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
use App\Framework\Attributes\Route;
use App\Framework\Core\AppBootstrapper;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Performance\MemoryMonitor;
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/src/Framework/Debug/helpers.php';
echo "DEBUG: Starting container debug\n";
// Create dependencies for enhanced performance collector
$basePath = __DIR__;
$clock = new SystemClock();
$highResClock = new SystemHighResolutionClock();
$memoryMonitor = new MemoryMonitor();
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: true);
echo "DEBUG: About to create AppBootstrapper\n";
$bootstrapper = new AppBootstrapper($basePath, $collector, $memoryMonitor);
echo "DEBUG: About to bootstrap web app\n";
$app = $bootstrapper->bootstrapWeb();
echo "DEBUG: Web app bootstrapped successfully\n";
// Get container from the app
$reflection = new ReflectionObject($app);
$containerProperty = $reflection->getProperty('container');
$containerProperty->setAccessible(true);
$container = $containerProperty->getValue($app);
echo "DEBUG: Got container from app\n";
// Check if DiscoveryRegistry is available
if ($container->has(DiscoveryRegistry::class)) {
echo "DEBUG: DiscoveryRegistry is available in container\n";
$registry = $container->get(DiscoveryRegistry::class);
$routeCount = $registry->attributes->getCount(Route::class);
echo "DEBUG: Found $routeCount route attributes in registry\n";
if ($routeCount > 0) {
$routes = $registry->attributes->get(Route::class);
echo "DEBUG: First few routes:\n";
foreach (array_slice($routes, 0, 3) as $i => $route) {
echo " Route $i: " . $route->className->getFullyQualified() . "::" . $route->methodName?->toString() . "\n";
}
// Look for the home route specifically
echo "DEBUG: Looking for home route (/)\n";
$homeFound = false;
foreach ($routes as $route) {
$instance = $route->createAttributeInstance();
if ($instance instanceof \App\Framework\Attributes\Route && $instance->path === '/') {
echo " HOME ROUTE FOUND: " . $route->className->getFullyQualified() . "::" . $route->methodName?->toString() . " -> " . $instance->path . "\n";
$homeFound = true;
}
}
if (!$homeFound) {
echo " HOME ROUTE NOT FOUND in discovery results\n";
}
} else {
echo "DEBUG: No routes found in registry\n";
}
echo "DEBUG: Total discovery items: " . count($registry) . "\n";
} else {
echo "DEBUG: DiscoveryRegistry is NOT available in container\n";
}
echo "DEBUG: Debug complete\n";

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
use App\Framework\Attributes\Route;
use App\Framework\Core\AppBootstrapper;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Performance\MemoryMonitor;
use App\Framework\DI\Initializer;
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/src/Framework/Debug/helpers.php';
echo "DEBUG: Starting context analysis\n";
// Create dependencies for enhanced performance collector
$basePath = __DIR__;
$clock = new SystemClock();
$highResClock = new SystemHighResolutionClock();
$memoryMonitor = new MemoryMonitor();
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: true);
echo "DEBUG: About to create AppBootstrapper\n";
$bootstrapper = new AppBootstrapper($basePath, $collector, $memoryMonitor);
echo "DEBUG: About to bootstrap web app\n";
$app = $bootstrapper->bootstrapWeb();
echo "DEBUG: Web app bootstrapped successfully\n";
// Get container from the app
$reflection = new ReflectionObject($app);
$containerProperty = $reflection->getProperty('container');
$containerProperty->setAccessible(true);
$container = $containerProperty->getValue($app);
echo "DEBUG: Got container from app\n";
// Check if DiscoveryRegistry is available
if ($container->has(DiscoveryRegistry::class)) {
echo "DEBUG: DiscoveryRegistry is available in container\n";
$registry = $container->get(DiscoveryRegistry::class);
$initializerCount = $registry->attributes->getCount(Initializer::class);
echo "DEBUG: Found $initializerCount initializer attributes in registry\n";
if ($initializerCount > 0) {
$initializers = $registry->attributes->get(Initializer::class);
echo "DEBUG: Looking for router-related initializers\n";
foreach ($initializers as $initializer) {
$className = $initializer->className->getFullyQualified();
if (str_contains($className, 'Router') || str_contains($className, 'Route')) {
echo " ROUTER INITIALIZER: $className\n";
// Get the attribute instance to see the contexts
$attributeInstance = $initializer->createAttributeInstance();
if ($attributeInstance instanceof \App\Framework\DI\Initializer) {
$contexts = $attributeInstance->contexts;
if (empty($contexts)) {
echo " CONTEXTS: ALL (no restrictions)\n";
} else {
$contextNames = array_map(function($ctx) {
return $ctx instanceof \App\Framework\Context\ContextType ? $ctx->name : (string)$ctx;
}, $contexts);
echo " CONTEXTS: " . implode(', ', $contextNames) . "\n";
}
}
// Check additionalData for debugging
if (isset($initializer->additionalData['contexts'])) {
echo " STORED CONTEXTS: " . print_r($initializer->additionalData['contexts'], true);
}
}
}
} else {
echo "DEBUG: No initializers found in registry\n";
}
} else {
echo "DEBUG: DiscoveryRegistry is NOT available in container\n";
}
echo "DEBUG: Context analysis complete\n";

View File

@@ -0,0 +1,94 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use App\Framework\Core\AppBootstrapper;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Performance\MemoryMonitor;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Console\ConsoleApplication;
try {
echo "1. Creating performance collector..." . PHP_EOL;
$clock = new SystemClock();
$highResClock = new SystemHighResolutionClock();
$memoryMonitor = new MemoryMonitor();
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: false);
echo "2. Creating bootstrapper..." . PHP_EOL;
$bootstrapper = new AppBootstrapper(__DIR__, $collector, $memoryMonitor);
echo "3. Bootstrapping console..." . PHP_EOL;
$consoleApp = $bootstrapper->bootstrapConsole();
echo "4. Getting container from console app..." . PHP_EOL;
$reflection = new ReflectionClass($consoleApp);
$containerProperty = $reflection->getProperty('container');
$containerProperty->setAccessible(true);
$container = $containerProperty->getValue($consoleApp);
echo "5. Checking if DiscoveryRegistry is in container..." . PHP_EOL;
$hasDiscoveryRegistry = $container->has(DiscoveryRegistry::class);
echo " DiscoveryRegistry available: " . ($hasDiscoveryRegistry ? 'YES' : 'NO') . PHP_EOL;
if ($hasDiscoveryRegistry) {
echo "6. Getting DiscoveryRegistry..." . PHP_EOL;
$discoveryRegistry = $container->get(DiscoveryRegistry::class);
echo " DiscoveryRegistry type: " . get_class($discoveryRegistry) . PHP_EOL;
echo " Total attributes: " . $discoveryRegistry->attributes->count() . PHP_EOL;
// Show all discovered attribute types
$allTypes = $discoveryRegistry->attributes->getAllTypes();
echo " All discovered attribute types: " . implode(', ', $allTypes) . PHP_EOL;
$commands = $discoveryRegistry->attributes->get(\App\Framework\Console\ConsoleCommand::class);
echo " ConsoleCommand attributes: " . count($commands) . PHP_EOL;
if (count($commands) > 0) {
foreach (array_slice($commands, 0, 5) as $discovered) {
$methodName = $discovered->methodName ? $discovered->methodName->toString() : '__invoke';
echo " - " . $discovered->className->getFullyQualified() . "::" . $methodName . PHP_EOL;
$command = $discovered->createAttributeInstance();
echo " Command: " . $command->name . PHP_EOL;
}
} else {
echo " No ConsoleCommand attributes found!" . PHP_EOL;
echo " This suggests ConsoleCommandMapper is not included or working properly" . PHP_EOL;
}
} else {
echo "6. DiscoveryRegistry NOT found in container!" . PHP_EOL;
}
echo "7. Checking ConsoleApplication's command registry..." . PHP_EOL;
$registryProperty = $reflection->getProperty('commandRegistry');
$registryProperty->setAccessible(true);
$commandRegistry = $registryProperty->getValue($consoleApp);
if ($commandRegistry === null) {
echo " CommandRegistry is NULL!" . PHP_EOL;
echo " This means initializeCommandRegistry was never called or failed" . PHP_EOL;
} else {
echo " CommandRegistry type: " . get_class($commandRegistry) . PHP_EOL;
// Try to get the command list from the registry
$commandRegistryReflection = new ReflectionClass($commandRegistry);
$commandListProperty = $commandRegistryReflection->getProperty('commandList');
$commandListProperty->setAccessible(true);
$commandList = $commandListProperty->getValue($commandRegistry);
if ($commandList === null) {
echo " CommandList is NULL in CommandRegistry!" . PHP_EOL;
} else {
echo " CommandList type: " . get_class($commandList) . PHP_EOL;
echo " Command count: " . $commandList->count() . PHP_EOL;
}
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . PHP_EOL;
echo "Stack trace: " . $e->getTraceAsString() . PHP_EOL;
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use App\Framework\Core\AppBootstrapper;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Performance\MemoryMonitor;
echo "Testing Queue Initializer Discovery...\n\n";
try {
// Create dependencies for enhanced performance collector
$clock = new SystemClock();
$highResClock = new SystemHighResolutionClock();
$memoryMonitor = new MemoryMonitor();
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: false);
// Bootstrap the framework
$bootstrapper = new AppBootstrapper(__DIR__, $collector, $memoryMonitor);
$container = $bootstrapper->bootstrapWorker();
echo "Framework bootstrapped successfully.\n";
// Check if Queue services are available
$services = [
'App\Framework\Queue\Interfaces\DistributedLockInterface',
'App\Framework\Queue\Services\WorkerRegistry',
'App\Framework\Queue\Services\JobDistributionService',
'App\Framework\Queue\Services\WorkerHealthCheckService',
'App\Framework\Queue\Services\FailoverRecoveryService',
'App\Framework\Queue\Contracts\JobProgressTrackerInterface',
'App\Framework\Queue\Contracts\DeadLetterQueueInterface',
'App\Framework\Queue\Services\JobMetricsManagerInterface',
'App\Framework\Queue\Contracts\JobDependencyManagerInterface'
];
echo "\nChecking Queue service registrations:\n";
echo str_repeat("=", 50) . "\n";
foreach ($services as $service) {
if ($container->has($service)) {
echo "{$service} - REGISTERED\n";
} else {
echo "{$service} - NOT REGISTERED\n";
}
}
echo "\nTotal container bindings: " . count($container->getBindings()) . "\n";
} catch (Throwable $e) {
echo "Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
}

View File

@@ -0,0 +1,263 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
// Live debug version of TUI that shows what's happening
use App\Framework\Console\Components\TuiState;
use App\Framework\Console\Components\TuiInputHandler;
use App\Framework\Console\Components\TuiCommandExecutor;
use App\Framework\Console\Components\TuiRenderer;
use App\Framework\Console\CommandHistory;
use App\Framework\Console\CommandGroupRegistry;
use App\Framework\Console\SimpleWorkflowExecutor;
use App\Framework\Console\TuiView;
use App\Framework\Console\TuiKeyCode;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\Screen\ScreenManager;
use App\Framework\Console\Screen\ScreenControlCode;
use App\Framework\Console\Screen\CursorControlCode;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\DI\Container;
echo "Live TUI Debug Session\n";
echo "======================\n\n";
// Check TTY
if (!posix_isatty(STDIN)) {
echo "❌ Requires TTY. Run with: docker exec -it php php debug_live_tui.php\n";
exit(1);
}
echo "✓ TTY available\n";
// Check PHPStorm
$isPhpStorm = getenv('TERMINAL_EMULATOR') === 'JetBrains-JediTerm';
echo "✓ PHPStorm detected: " . ($isPhpStorm ? "YES" : "NO") . "\n\n";
try {
// Create minimal dependencies
$container = new \App\Framework\DI\DefaultContainer();
$discoveryRegistry = new class implements DiscoveryRegistry {
public function interfaces() {
return new class { public function get(string $interface) { return []; } };
}
public function attributes() {
return new class { public function get(string $attributeClass) { return []; } };
}
};
// Debug command executor
$debugExecutor = new class implements TuiCommandExecutor {
public function executeSelectedCommand(object $command): void {
echo "\n🚀 EXECUTE: Selected command\n";
sleep(1);
}
public function executeCommand(string $commandName): void {
echo "\n🚀 EXECUTE: $commandName\n";
sleep(1);
}
public function validateSelectedCommand(object $command): void {
echo "\n✓ VALIDATE: Selected command\n";
sleep(1);
}
public function validateCommand(string $commandName): void {
echo "\n✓ VALIDATE: $commandName\n";
sleep(1);
}
public function showSelectedCommandHelp(object $command): void {
echo "\n📚 HELP: Selected command\n";
sleep(1);
}
public function showCommandHelp(string $commandName): void {
echo "\n📚 HELP: $commandName\n";
sleep(1);
}
public function showAllCommandsHelp(): void {
echo "\n📚 HELP: All commands\n";
sleep(1);
}
public function startInteractiveForm(object $command, \App\Framework\Console\Components\TuiState $state): void {
echo "\n📝 FORM: Start interactive form\n";
sleep(1);
}
public function findCommandObject(string $commandName): ?object {
return null;
}
};
$workflowExecutor = new class implements SimpleWorkflowExecutor {
public function executeWorkflow(array $workflow, \App\Framework\Console\Components\TuiState $state): void {
echo "\n🔄 WORKFLOW: Execute\n";
sleep(1);
}
};
// Create output with debug info
$output = new class extends ConsoleOutput {
public function writeLine(string $line = '', ?ConsoleColor $color = null): void {
// Show debug info for navigation updates
if (str_contains($line, '▶')) {
echo "\n[DEBUG] Navigation update detected: $line\n";
}
parent::writeLine($line, $color);
}
};
$output->screen = new ScreenManager($output);
// Custom TUI state with debug
$debugState = new class extends TuiState {
public function setSelectedCategory(int $index): void {
$oldIndex = $this->getSelectedCategory();
parent::setSelectedCategory($index);
$newIndex = $this->getSelectedCategory();
echo "\n[DEBUG] Category changed: $oldIndex$newIndex\n";
}
public function navigateUp(): void {
echo "\n[DEBUG] navigateUp() called\n";
$before = $this->getSelectedCategory();
parent::navigateUp();
$after = $this->getSelectedCategory();
echo "[DEBUG] navigateUp result: $before$after\n";
}
public function navigateDown(): void {
echo "\n[DEBUG] navigateDown() called\n";
$before = $this->getSelectedCategory();
parent::navigateDown();
$after = $this->getSelectedCategory();
echo "[DEBUG] navigateDown result: $before$after\n";
}
};
// Custom input handler with debug
$debugInputHandler = new class($debugExecutor) extends TuiInputHandler {
public function handleInput(string $key, \App\Framework\Console\Components\TuiState $state, \App\Framework\Console\CommandHistory $history): void {
$keyHex = bin2hex($key);
$keyDesc = match($key) {
"\033[A" => "ARROW_UP",
"\033[B" => "ARROW_DOWN",
"\033[C" => "ARROW_RIGHT",
"\033[D" => "ARROW_LEFT",
"\n" => "ENTER",
" " => "SPACE",
"\033" => "ESC",
'q' => "Q",
default => "OTHER($key)"
};
echo "\n[DEBUG] INPUT: '$keyDesc' (hex: $keyHex) in view: " . $state->getCurrentView()->name . "\n";
parent::handleInput($key, $state, $history);
echo "[DEBUG] After input - Selected: " . $state->getSelectedCategory() . "\n";
}
};
$state = $debugState;
$history = new CommandHistory();
$inputHandler = $debugInputHandler;
$renderer = new TuiRenderer($output);
$groupRegistry = new CommandGroupRegistry($discoveryRegistry);
// Add test categories
$testCategories = [
['name' => 'Testing', 'description' => 'Test commands', 'icon' => '🧪', 'commands' => []],
['name' => 'Demo', 'description' => 'Demo commands', 'icon' => '🎮', 'commands' => []],
['name' => 'Generator', 'description' => 'Code generation', 'icon' => '⚙️', 'commands' => []],
['name' => 'General', 'description' => 'General commands', 'icon' => '📂', 'commands' => []]
];
$state->setCategories($testCategories);
$state->setCurrentView(TuiView::CATEGORIES);
$state->setRunning(true);
echo "✓ Debug TUI components ready\n";
echo "Categories: " . count($testCategories) . "\n";
echo "Selected: " . $state->getSelectedCategory() . "\n\n";
// Save terminal settings
$originalSettings = trim(shell_exec('stty -g') ?: '');
// Set up terminal
if ($isPhpStorm) {
shell_exec('stty raw -echo min 1 time 0 2>/dev/null');
} else {
shell_exec('stty -icanon -echo 2>/dev/null');
}
// Hide cursor
echo CursorControlCode::HIDE->format();
echo "🚀 LIVE DEBUG TUI STARTED\n";
echo "Use arrow keys to test navigation.\n";
echo "Press 'q' to quit.\n";
echo "All navigation events will be logged below.\n\n";
// Simple TUI loop with debug
while ($state->isRunning()) {
// Clear and render
echo ScreenControlCode::CLEAR_ALL->format();
echo CursorControlCode::POSITION->format(1, 1);
// Render current state
$renderer->render($state, $history);
// Show debug info at bottom
echo "\n" . str_repeat('=', 60) . "\n";
echo "DEBUG INFO:\n";
echo "Selected Category: " . $state->getSelectedCategory() . "\n";
echo "Current View: " . $state->getCurrentView()->name . "\n";
$category = $state->getCurrentCategory();
echo "Category Name: " . ($category ? $category['name'] : 'NULL') . "\n";
echo "Press arrow keys to test navigation...\n";
// Read input with PHPStorm-compatible method
$key = fgetc(STDIN);
if ($key === false) continue;
if ($key === "\033") {
$sequence = $key;
stream_set_blocking(STDIN, false);
$next = fgetc(STDIN);
if ($next === false) {
usleep(10000);
$next = fgetc(STDIN);
}
if ($next !== false) {
$sequence .= $next;
if ($next === '[') {
$third = fgetc(STDIN);
if ($third === false) {
usleep(10000);
$third = fgetc(STDIN);
}
if ($third !== false) {
$sequence .= $third;
}
}
}
stream_set_blocking(STDIN, true);
$key = $sequence;
}
if ($key === 'q' || $key === 'Q') {
$state->setRunning(false);
break;
}
// Process input
$inputHandler->handleInput($key, $state, $history);
}
} finally {
// Restore terminal
echo CursorControlCode::SHOW->format();
if (!empty($originalSettings)) {
shell_exec("stty $originalSettings 2>/dev/null");
}
echo "\n✓ Debug TUI session ended\n";
}

View File

@@ -0,0 +1,99 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use App\Framework\Core\AppBootstrapper;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Performance\MemoryMonitor;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Context\ExecutionContext;
echo "=== Debug: Make Console Issue ===" . PHP_EOL;
echo "Current environment:" . PHP_EOL;
echo " TERM: " . ($_SERVER['TERM'] ?? 'not set') . PHP_EOL;
echo " STDIN: " . (stream_isatty(STDIN) ? 'TTY' : 'not TTY') . PHP_EOL;
echo " STDOUT: " . (stream_isatty(STDOUT) ? 'TTY' : 'not TTY') . PHP_EOL;
echo " argv[0]: " . ($_SERVER['argv'][0] ?? 'not set') . PHP_EOL;
echo " Command line: " . implode(' ', $_SERVER['argv'] ?? []) . PHP_EOL;
try {
echo "1. Creating components..." . PHP_EOL;
$clock = new SystemClock();
$highResClock = new SystemHighResolutionClock();
$memoryMonitor = new MemoryMonitor();
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: false);
$bootstrapper = new AppBootstrapper(__DIR__, $collector, $memoryMonitor);
echo "2. Detecting execution context..." . PHP_EOL;
$context = ExecutionContext::detect();
echo " Context type: " . $context->getType()->value . PHP_EOL;
echo "3. Bootstrapping console..." . PHP_EOL;
$consoleApp = $bootstrapper->bootstrapConsole();
echo "4. Getting container..." . PHP_EOL;
$reflection = new ReflectionClass($consoleApp);
$containerProperty = $reflection->getProperty('container');
$containerProperty->setAccessible(true);
$container = $containerProperty->getValue($consoleApp);
echo "5. Checking DiscoveryRegistry..." . PHP_EOL;
if ($container->has(DiscoveryRegistry::class)) {
$discoveryRegistry = $container->get(DiscoveryRegistry::class);
$commands = $discoveryRegistry->attributes->get(ConsoleCommand::class);
echo " ConsoleCommand attributes: " . count($commands) . PHP_EOL;
if (count($commands) > 0) {
echo " First 3 commands:" . PHP_EOL;
foreach (array_slice($commands, 0, 3) as $discovered) {
$command = $discovered->createAttributeInstance();
echo " - " . $command->name . PHP_EOL;
}
}
} else {
echo " DiscoveryRegistry NOT found!" . PHP_EOL;
}
echo "6. Checking CommandRegistry..." . PHP_EOL;
$registryProperty = $reflection->getProperty('commandRegistry');
$registryProperty->setAccessible(true);
$commandRegistry = $registryProperty->getValue($consoleApp);
if ($commandRegistry) {
$commandRegistryReflection = new ReflectionClass($commandRegistry);
$commandListProperty = $commandRegistryReflection->getProperty('commandList');
$commandListProperty->setAccessible(true);
$commandList = $commandListProperty->getValue($commandRegistry);
if ($commandList) {
echo " CommandList count: " . $commandList->count() . PHP_EOL;
} else {
echo " CommandList is NULL!" . PHP_EOL;
}
} else {
echo " CommandRegistry is NULL!" . PHP_EOL;
}
echo "7. Cache key analysis..." . PHP_EOL;
$pathProvider = $container->get(\App\Framework\Core\PathProvider::class);
$currentContext = ExecutionContext::detect();
$contextString = $currentContext->getType()->value;
$defaultPaths = [$pathProvider->getSourcePath()];
echo " Context: " . $contextString . PHP_EOL;
echo " Paths: " . implode(', ', $defaultPaths) . PHP_EOL;
// Generate cache key as it would be done in DiscoveryServiceBootstrapper
$pathsHash = md5(implode('|', $defaultPaths));
$cacheKey = "discovery:full_{$pathsHash}_{$contextString}";
echo " Expected cache key: " . $cacheKey . PHP_EOL;
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . PHP_EOL;
echo "Stack trace: " . $e->getTraceAsString() . PHP_EOL;
}

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use App\Framework\Console\ConsoleCommandMapper;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\DemoCommand;
use App\Framework\Reflection\WrappedReflectionMethod;
echo "Testing ConsoleCommandMapper..." . PHP_EOL;
try {
$mapper = new ConsoleCommandMapper();
// Test the mapper configuration
echo "Mapper attribute class: " . $mapper->getAttributeClass() . PHP_EOL;
echo "ConsoleCommand class: " . ConsoleCommand::class . PHP_EOL;
echo "Classes match: " . ($mapper->getAttributeClass() === ConsoleCommand::class ? 'YES' : 'NO') . PHP_EOL;
// Test with a real method
$reflector = new ReflectionClass(DemoCommand::class);
$method = $reflector->getMethod('hello');
$attributes = $method->getAttributes(ConsoleCommand::class);
if (!empty($attributes)) {
$attribute = $attributes[0]->newInstance();
echo "Attribute found: " . $attribute->name . PHP_EOL;
// This would require complex setup, just test the basic mapping
echo "Basic mapper test passed!" . PHP_EOL;
} else {
echo "No attributes found on hello method!" . PHP_EOL;
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . PHP_EOL;
echo "Trace: " . $e->getTraceAsString() . PHP_EOL;
}

View File

@@ -0,0 +1,167 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
// Debug navigation issue
use App\Framework\Console\Components\TuiState;
use App\Framework\Console\Components\TuiInputHandler;
use App\Framework\Console\Components\TuiCommandExecutor;
use App\Framework\Console\CommandHistory;
use App\Framework\Console\TuiView;
use App\Framework\Console\TuiKeyCode;
echo "Debugging TUI Navigation Issue...\n";
echo "=================================\n\n";
try {
// Create mock executor
$mockExecutor = new class implements \App\Framework\Console\Components\TuiCommandExecutor {
public function executeSelectedCommand(object $command): void {
echo "Mock: Execute command\n";
}
public function executeCommand(string $commandName): void {
echo "Mock: Execute command: $commandName\n";
}
public function validateSelectedCommand(object $command): void {
echo "Mock: Validate command\n";
}
public function validateCommand(string $commandName): void {
echo "Mock: Validate command: $commandName\n";
}
public function showSelectedCommandHelp(object $command): void {
echo "Mock: Show help\n";
}
public function showCommandHelp(string $commandName): void {
echo "Mock: Show help: $commandName\n";
}
public function showAllCommandsHelp(): void {
echo "Mock: Show all commands help\n";
}
public function startInteractiveForm(object $command, \App\Framework\Console\Components\TuiState $state): void {
echo "Mock: Start form\n";
}
public function findCommandObject(string $commandName): ?object {
return null;
}
};
$state = new TuiState();
$history = new CommandHistory();
$inputHandler = new TuiInputHandler($mockExecutor);
// Setup realistic test categories like in real TUI
$categories = [
0 => [
'name' => 'Database',
'description' => 'Database commands',
'icon' => '🗄️',
'commands' => [],
'priority' => 100
],
1 => [
'name' => 'Cache',
'description' => 'Cache commands',
'icon' => '⚡',
'commands' => [],
'priority' => 90
],
2 => [
'name' => 'Testing',
'description' => 'Testing commands',
'icon' => '🧪',
'commands' => [],
'priority' => 80
],
3 => [
'name' => 'MCP',
'description' => 'MCP commands',
'icon' => '🤖',
'commands' => [],
'priority' => 70
]
];
$state->setCategories($categories);
$state->setCurrentView(TuiView::CATEGORIES);
$state->setRunning(true);
echo "✓ Initial Setup:\n";
echo " Categories: " . count($categories) . "\n";
echo " Current View: " . $state->getCurrentView()->name . "\n";
echo " Selected Category: " . $state->getSelectedCategory() . "\n";
echo " Category Name: '{$categories[$state->getSelectedCategory()]['name']}'\n\n";
// Test step-by-step navigation
echo "🔍 Testing Navigation Step-by-Step:\n\n";
// Test 1: Arrow Down
echo "Test 1: Arrow DOWN\n";
echo " Before: Category {$state->getSelectedCategory()} ('{$categories[$state->getSelectedCategory()]['name']}')\n";
echo " Input Key: '" . TuiKeyCode::ARROW_DOWN->value . "' (hex: " . bin2hex(TuiKeyCode::ARROW_DOWN->value) . ")\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
echo " After: Category {$state->getSelectedCategory()} ('{$categories[$state->getSelectedCategory()]['name']}')\n";
echo " ✓ Expected: Should move from Database (0) to Cache (1)\n\n";
// Test 2: Arrow Down again
echo "Test 2: Arrow DOWN again\n";
echo " Before: Category {$state->getSelectedCategory()} ('{$categories[$state->getSelectedCategory()]['name']}')\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
echo " After: Category {$state->getSelectedCategory()} ('{$categories[$state->getSelectedCategory()]['name']}')\n";
echo " ✓ Expected: Should move from Cache (1) to Testing (2)\n\n";
// Test 3: Arrow Up
echo "Test 3: Arrow UP\n";
echo " Before: Category {$state->getSelectedCategory()} ('{$categories[$state->getSelectedCategory()]['name']}')\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_UP->value, $state, $history);
echo " After: Category {$state->getSelectedCategory()} ('{$categories[$state->getSelectedCategory()]['name']}')\n";
echo " ✓ Expected: Should move from Testing (2) to Cache (1)\n\n";
// Test 4: Boundary testing - go to end
echo "Test 4: Go to last category and test boundary\n";
$state->setSelectedCategory(3); // MCP
echo " Set to: Category {$state->getSelectedCategory()} ('{$categories[$state->getSelectedCategory()]['name']}')\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
echo " After Arrow DOWN: Category {$state->getSelectedCategory()} ('{$categories[$state->getSelectedCategory()]['name']}')\n";
echo " ✓ Expected: Should stay at MCP (3) - boundary protection\n\n";
// Test 5: Boundary testing - go to beginning
echo "Test 5: Go to first category and test boundary\n";
$state->setSelectedCategory(0); // Database
echo " Set to: Category {$state->getSelectedCategory()} ('{$categories[$state->getSelectedCategory()]['name']}')\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_UP->value, $state, $history);
echo " After Arrow UP: Category {$state->getSelectedCategory()} ('{$categories[$state->getSelectedCategory()]['name']}')\n";
echo " ✓ Expected: Should stay at Database (0) - boundary protection\n\n";
// Debug the TuiState navigation methods directly
echo "🔍 Testing TuiState Navigation Methods Directly:\n\n";
echo "Direct TuiState Testing:\n";
$state->setSelectedCategory(1); // Cache
echo " Set to: {$state->getSelectedCategory()}\n";
echo " Calling navigateDown()...\n";
$state->navigateDown();
echo " Result: {$state->getSelectedCategory()}\n";
echo " Calling navigateUp()...\n";
$state->navigateUp();
echo " Result: {$state->getSelectedCategory()}\n\n";
echo "✅ Navigation Debug Test COMPLETED\n";
} catch (\Throwable $e) {
echo "\n❌ Navigation Debug Test FAILED:\n";
echo "Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n" . $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,80 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
// Debug das TUI Navigation Problem
use App\Framework\Console\Components\TuiState;
use App\Framework\Console\TuiView;
use App\Framework\Console\CommandGroupRegistry;
use App\Framework\Discovery\Results\DiscoveryRegistry;
echo "=== TUI NAVIGATION DEBUG ===\n";
try {
// Mock Discovery Registry
$discoveryRegistry = new class implements DiscoveryRegistry {
public function interfaces() {
return new class {
public function get(string $interface) { return []; }
};
}
public function attributes() {
return new class {
public function get(string $attributeClass) { return []; }
};
}
};
$groupRegistry = new CommandGroupRegistry($discoveryRegistry);
$categories = $groupRegistry->getOrganizedCommands();
echo "📊 Categories loaded: " . count($categories) . "\n";
foreach ($categories as $index => $category) {
echo " [$index] {$category['name']} - Commands: " . count($category['commands']) . "\n";
}
echo "\n";
// Test TuiState Navigation
$state = new TuiState();
$state->setCategories($categories);
$state->setCurrentView(TuiView::CATEGORIES);
echo "🔍 Initial TUI State:\n";
echo " Selected Category: " . $state->getSelectedCategory() . "\n";
echo " Current View: " . $state->getCurrentView()->name . "\n";
echo " Current Category: " . ($state->getCurrentCategory()['name'] ?? 'NULL') . "\n";
echo "\n";
// Test Navigation Up/Down
echo "🧪 Testing Navigation:\n";
for ($i = 0; $i < 3; $i++) {
echo "Step $i - Before navigateDown(): " . $state->getSelectedCategory() . "\n";
$state->navigateDown();
echo "Step $i - After navigateDown(): " . $state->getSelectedCategory() . "\n";
}
echo "\n";
for ($i = 0; $i < 5; $i++) {
echo "Step $i - Before navigateUp(): " . $state->getSelectedCategory() . "\n";
$state->navigateUp();
echo "Step $i - After navigateUp(): " . $state->getSelectedCategory() . "\n";
}
echo "\n";
// Test Category Bounds
echo "🎯 Testing Bounds:\n";
$state->setSelectedCategory(99);
echo "Set to 99, actual: " . $state->getSelectedCategory() . "\n";
$state->setSelectedCategory(-5);
echo "Set to -5, actual: " . $state->getSelectedCategory() . "\n";
echo "\n✅ Navigation logic test completed\n";
} catch (\Throwable $e) {
echo "❌ ERROR: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,171 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
// Debug real TUI execution
use App\Framework\Console\Components\ConsoleTUI;
use App\Framework\Console\Components\TuiState;
use App\Framework\Console\Components\TuiInputHandler;
use App\Framework\Console\Components\TuiCommandExecutor;
use App\Framework\Console\Components\TuiRenderer;
use App\Framework\Console\CommandHistory;
use App\Framework\Console\CommandGroupRegistry;
use App\Framework\Console\SimpleWorkflowExecutor;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\Screen\ScreenManager;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\DI\Container;
echo "Debug Real TUI Execution\n";
echo "========================\n\n";
// Check TTY first
if (!posix_isatty(STDIN)) {
echo "❌ This test requires a TTY terminal.\n";
echo "Run with: docker exec -it php php debug_real_tui.php\n";
exit(1);
}
echo "✓ TTY detected\n";
// Check stty
if (!function_exists('shell_exec') || shell_exec('which stty') === null) {
echo "❌ stty not available\n";
exit(1);
}
echo "✓ stty available\n\n";
try {
// Create minimal dependencies
$container = new \App\Framework\DI\DefaultContainer();
// Create a minimal discovery registry
$discoveryRegistry = new class implements DiscoveryRegistry {
public function interfaces() {
return new class {
public function get(string $interface) { return []; }
};
}
public function attributes() {
return new class {
public function get(string $attributeClass) { return []; }
};
}
};
// Create mock command executor
$mockExecutor = new class implements TuiCommandExecutor {
public function executeSelectedCommand(object $command): void {
echo "\n🚀 Mock: Execute command\n";
}
public function executeCommand(string $commandName): void {
echo "\n🚀 Mock: Execute command: $commandName\n";
}
public function validateSelectedCommand(object $command): void {
echo "\n✓ Mock: Validate command\n";
}
public function validateCommand(string $commandName): void {
echo "\n✓ Mock: Validate command: $commandName\n";
}
public function showSelectedCommandHelp(object $command): void {
echo "\n📚 Mock: Show help\n";
}
public function showCommandHelp(string $commandName): void {
echo "\n📚 Mock: Show help: $commandName\n";
}
public function showAllCommandsHelp(): void {
echo "\n📚 Mock: Show all commands help\n";
}
public function startInteractiveForm(object $command, \App\Framework\Console\Components\TuiState $state): void {
echo "\n📝 Mock: Start form\n";
}
public function findCommandObject(string $commandName): ?object {
return null;
}
};
// Create workflow executor
$workflowExecutor = new class implements SimpleWorkflowExecutor {
public function executeWorkflow(array $workflow, \App\Framework\Console\Components\TuiState $state): void {
echo "\n🔄 Mock: Execute workflow\n";
}
};
// Create components
$output = new ConsoleOutput();
$output->screen = new ScreenManager($output);
$state = new TuiState();
$history = new CommandHistory();
$inputHandler = new TuiInputHandler($mockExecutor);
$renderer = new TuiRenderer($output);
$groupRegistry = new CommandGroupRegistry($discoveryRegistry);
echo "✓ Components created\n";
// Test categories loading
$categories = $groupRegistry->getOrganizedCommands();
echo "✓ Categories loaded: " . count($categories) . "\n";
if (empty($categories)) {
// Add test categories since discovery is empty
$testCategories = [
[
'name' => 'Testing',
'description' => 'Test commands',
'icon' => '🧪',
'priority' => 0,
'commands' => []
],
[
'name' => 'Demo',
'description' => 'Demo commands',
'icon' => '🎮',
'priority' => 0,
'commands' => []
],
[
'name' => 'General',
'description' => 'General commands',
'icon' => '📂',
'priority' => 0,
'commands' => []
]
];
$state->setCategories($testCategories);
echo "✓ Test categories added: " . count($testCategories) . "\n";
} else {
$state->setCategories($categories);
}
// Create TUI
$tui = new ConsoleTUI(
$output,
$container,
$discoveryRegistry,
$state,
$renderer,
$inputHandler,
$mockExecutor,
$history,
$groupRegistry,
$workflowExecutor
);
echo "✓ TUI created\n\n";
echo "🚀 Starting TUI...\n";
echo "Use arrow keys to navigate, 'q' to quit.\n";
echo "This will show if the TUI actually responds to input.\n\n";
// Start TUI
$exitCode = $tui->run();
echo "\n✓ TUI exited with code: " . $exitCode->value . "\n";
} catch (\Throwable $e) {
echo "\n❌ ERROR: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n" . $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,56 @@
<?php
require '/var/www/html/vendor/autoload.php';
use App\Framework\Discovery\DiscoveryServiceBootstrapper;
use App\Framework\DI\DefaultContainer;
use App\Framework\Attributes\Route;
try {
echo "=== Route Discovery Diagnostic ===\n";
$container = new DefaultContainer();
$bootStrapper = new DiscoveryServiceBootstrapper();
$bootStrapper->initialize($container);
$registry = $container->get('App\Framework\Discovery\Results\DiscoveryRegistry');
$routes = $registry->attributes->get(Route::class);
echo "Total routes found: " . count($routes) . "\n\n";
$adminRoutes = [];
foreach ($routes as $route) {
$path = $route->additionalData['path'] ?? '';
if (str_contains($path, 'admin')) {
$adminRoutes[] = [
'path' => $path,
'controller' => $route->className->getFullyQualified(),
'method' => $route->methodName?->toString() ?? 'unknown'
];
}
}
echo "Admin routes found:\n";
foreach ($adminRoutes as $route) {
echo " Path: {$route['path']}\n";
echo " Controller: {$route['controller']}\n";
echo " Method: {$route['method']}\n";
echo " ---\n";
}
// Check specifically for ShowRoutes
echo "\nLooking for ShowRoutes controller:\n";
foreach ($routes as $route) {
if (str_contains($route->className->getFullyQualified(), 'ShowRoutes')) {
echo "Found ShowRoutes route:\n";
echo " Path: " . ($route->additionalData['path'] ?? 'unknown') . "\n";
echo " Controller: " . $route->className->getFullyQualified() . "\n";
echo " Method: " . ($route->methodName?->toString() ?? 'unknown') . "\n";
break;
}
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
echo "Stack trace: " . $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,34 @@
<?php
require __DIR__ . '/vendor/autoload.php';
use App\Framework\Core\Application;
use App\Framework\View\TemplateRenderer;
try {
$app = new Application();
$container = $app->getContainer();
$templateRenderer = $container->get(TemplateRenderer::class);
echo "TemplateRenderer class: " . get_class($templateRenderer) . "\n";
echo "TemplateRenderer instance of TemplateRenderer: " . ($templateRenderer instanceof TemplateRenderer ? 'YES' : 'NO') . "\n";
if (method_exists($templateRenderer, 'render')) {
echo "Has render method: YES\n";
} else {
echo "Has render method: NO\n";
}
// Check if it's the Engine class we expect
if (get_class($templateRenderer) === 'App\Framework\View\Engine') {
echo "Is Engine class: YES\n";
} else {
echo "Is Engine class: NO\n";
echo "Actual class: " . get_class($templateRenderer) . "\n";
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
echo "Trace: " . $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,219 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
// Debug TUI navigation logic specifically
use App\Framework\Console\Components\TuiState;
use App\Framework\Console\Components\TuiInputHandler;
use App\Framework\Console\Components\TuiCommandExecutor;
use App\Framework\Console\CommandHistory;
use App\Framework\Console\CommandGroupRegistry;
use App\Framework\Console\TuiView;
use App\Framework\Console\TuiKeyCode;
use App\Framework\Discovery\Results\DiscoveryRegistry;
echo "Debug TUI Navigation Logic\n";
echo "==========================\n\n";
try {
// Create discovery registry that returns real-looking data
$discoveryRegistry = new class implements DiscoveryRegistry {
public function interfaces() {
return new class {
public function get(string $interface) { return []; }
};
}
public function attributes() {
return new class {
public function get(string $attributeClass) { return []; }
};
}
};
// Create mock executor with debug output
$mockExecutor = new class implements TuiCommandExecutor {
public function executeSelectedCommand(object $command): void {
echo "\n🚀 Mock: Execute command\n";
}
public function executeCommand(string $commandName): void {
echo "\n🚀 Mock: Execute command: $commandName\n";
}
public function validateSelectedCommand(object $command): void {
echo "\n✓ Mock: Validate command\n";
}
public function validateCommand(string $commandName): void {
echo "\n✓ Mock: Validate command: $commandName\n";
}
public function showSelectedCommandHelp(object $command): void {
echo "\n📚 Mock: Show help\n";
}
public function showCommandHelp(string $commandName): void {
echo "\n📚 Mock: Show help: $commandName\n";
}
public function showAllCommandsHelp(): void {
echo "\n📚 Mock: Show all commands help\n";
}
public function startInteractiveForm(object $command, \App\Framework\Console\Components\TuiState $state): void {
echo "\n📝 Mock: Start form\n";
}
public function findCommandObject(string $commandName): ?object {
return null;
}
};
// Create components
$state = new TuiState();
$history = new CommandHistory();
$inputHandler = new TuiInputHandler($mockExecutor);
$groupRegistry = new CommandGroupRegistry($discoveryRegistry);
echo "✓ Components created\n";
// Since discovery is empty, let's manually create categories like the real TUI
$testCategories = [
[
'name' => 'Testing',
'description' => 'Test commands',
'icon' => '🧪',
'priority' => 0,
'commands' => []
],
[
'name' => 'Demo',
'description' => 'Demo commands',
'icon' => '🎮',
'priority' => 0,
'commands' => []
],
[
'name' => 'Generator',
'description' => 'Code generation',
'icon' => '⚙️',
'priority' => 0,
'commands' => []
],
[
'name' => 'General',
'description' => 'General commands',
'icon' => '📂',
'priority' => 0,
'commands' => []
]
];
echo "📊 Test Categories:\n";
foreach ($testCategories as $index => $category) {
echo " [$index] {$category['icon']} {$category['name']} - {$category['description']}\n";
}
echo "\n";
// Setup TUI state
$state->setCategories($testCategories);
$state->setCurrentView(TuiView::CATEGORIES);
$state->setRunning(true);
echo "✓ TUI State initialized:\n";
echo " Categories: " . count($testCategories) . "\n";
echo " Current View: " . $state->getCurrentView()->name . "\n";
echo " Selected Category: " . $state->getSelectedCategory() . "\n";
$currentCategory = $state->getCurrentCategory();
if ($currentCategory) {
echo " Current Category: '{$currentCategory['name']}'\n";
} else {
echo " ❌ ERROR: getCurrentCategory() returned NULL!\n";
echo " This would cause navigation to fail!\n";
}
echo "\n=== STEP-BY-STEP NAVIGATION DEBUG ===\n\n";
function debugState($state, $step) {
echo "Step $step State:\n";
echo " - Selected Index: " . $state->getSelectedCategory() . "\n";
echo " - Current View: " . $state->getCurrentView()->name . "\n";
$category = $state->getCurrentCategory();
if ($category) {
echo " - Current Category: '{$category['name']}'\n";
} else {
echo " - ❌ Current Category: NULL\n";
}
echo "\n";
}
// Initial state
debugState($state, "Initial");
// Test 1: Direct TuiState navigation
echo "🔍 Test 1: Direct TuiState navigation\n";
echo "Calling \$state->navigateDown()...\n";
$state->navigateDown();
debugState($state, "After navigateDown()");
echo "Calling \$state->navigateUp()...\n";
$state->navigateUp();
debugState($state, "After navigateUp()");
// Test 2: TuiInputHandler navigation
echo "🔍 Test 2: TuiInputHandler with Arrow Keys\n";
echo "Simulating ARROW_DOWN input...\n";
echo "Key code: '" . TuiKeyCode::ARROW_DOWN->value . "' (hex: " . bin2hex(TuiKeyCode::ARROW_DOWN->value) . ")\n";
$beforeIndex = $state->getSelectedCategory();
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
$afterIndex = $state->getSelectedCategory();
echo "Before: $beforeIndex, After: $afterIndex\n";
echo "Changed: " . ($beforeIndex !== $afterIndex ? "YES ✓" : "NO ❌") . "\n";
debugState($state, "After ARROW_DOWN");
echo "Simulating ARROW_UP input...\n";
echo "Key code: '" . TuiKeyCode::ARROW_UP->value . "' (hex: " . bin2hex(TuiKeyCode::ARROW_UP->value) . ")\n";
$beforeIndex = $state->getSelectedCategory();
$inputHandler->handleInput(TuiKeyCode::ARROW_UP->value, $state, $history);
$afterIndex = $state->getSelectedCategory();
echo "Before: $beforeIndex, After: $afterIndex\n";
echo "Changed: " . ($beforeIndex !== $afterIndex ? "YES ✓" : "NO ❌") . "\n";
debugState($state, "After ARROW_UP");
// Test 3: Check bounds
echo "🔍 Test 3: Boundary testing\n";
// Go to last category
$lastIndex = count($testCategories) - 1;
$state->setSelectedCategory($lastIndex);
echo "Set to last category ($lastIndex)\n";
debugState($state, "Set to last");
echo "Try to go beyond last (ARROW_DOWN)...\n";
$beforeIndex = $state->getSelectedCategory();
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
$afterIndex = $state->getSelectedCategory();
echo "Before: $beforeIndex, After: $afterIndex\n";
echo "Boundary protected: " . ($beforeIndex === $afterIndex ? "YES ✓" : "NO ❌") . "\n";
// Test 4: Check TuiInputHandler logic
echo "\n🔍 Test 4: Debug TuiInputHandler logic\n";
// Let's trace what happens in handleInput
echo "Current view check: " . $state->getCurrentView()->name . "\n";
echo "Is CATEGORIES view: " . ($state->getCurrentView() === TuiView::CATEGORIES ? "YES" : "NO") . "\n";
if ($state->getCurrentView() === TuiView::CATEGORIES) {
echo "✓ View is correct for category navigation\n";
} else {
echo "❌ View is not CATEGORIES - navigation won't work!\n";
}
echo "\n✅ NAVIGATION DEBUG COMPLETED\n";
} catch (\Throwable $e) {
echo "\n❌ DEBUG FAILED:\n";
echo "Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n" . $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,27 @@
<?php
// Simple production test endpoint
declare(strict_types=1);
// Load environment
$envFile = __DIR__ . '/../.env';
if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos($line, '#') === 0) continue;
if (strpos($line, '=') !== false) {
[$key, $value] = explode('=', $line, 2);
$_ENV[trim($key)] = trim($value);
}
}
}
header('Content-Type: application/json');
echo json_encode([
'status' => 'production-test',
'app_env' => $_ENV['APP_ENV'] ?? 'not-set',
'app_debug' => $_ENV['APP_DEBUG'] ?? 'not-set',
'analytics_track_performance' => $_ENV['ANALYTICS_TRACK_PERFORMANCE'] ?? 'not-set',
'xdebug_mode' => $_ENV['XDEBUG_MODE'] ?? 'not-set',
'timestamp' => date('Y-m-d H:i:s')
]);

View File

@@ -0,0 +1,62 @@
<?php
// Force production environment test by reading .env directly
declare(strict_types=1);
// Load environment from .env file (override container env)
$envFile = __DIR__ . '/../.env';
if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos($line, '#') === 0) continue;
if (strpos($line, '=') !== false) {
[$key, $value] = explode('=', $line, 2);
$key = trim($key);
$value = trim($value);
// Override environment
$_ENV[$key] = $value;
putenv("$key=$value");
}
}
}
header('Content-Type: application/json');
$tests = [
'environment' => [
'app_env_file' => $_ENV['APP_ENV'] ?? 'not-set',
'app_debug_file' => $_ENV['APP_DEBUG'] ?? 'not-set',
'app_env_server' => $_SERVER['APP_ENV'] ?? 'not-set',
'app_debug_server' => $_SERVER['APP_DEBUG'] ?? 'not-set',
'status' => ($_ENV['APP_ENV'] ?? '') === 'production' && ($_ENV['APP_DEBUG'] ?? '') === 'false' ? 'PASS' : 'FAIL'
],
'performance_debug' => [
'analytics_track_performance' => $_ENV['ANALYTICS_TRACK_PERFORMANCE'] ?? 'not-set',
'status' => (strpos($_ENV['ANALYTICS_TRACK_PERFORMANCE'] ?? '', 'false') !== false) ? 'PASS' : 'FAIL'
],
'xdebug' => [
'xdebug_mode' => $_ENV['XDEBUG_MODE'] ?? 'not-set',
'status' => ($_ENV['XDEBUG_MODE'] ?? '') === 'off' ? 'PASS' : 'FAIL'
],
'files' => [
'env_file_exists' => file_exists($envFile) ? 'PASS' : 'FAIL',
'env_backup_created' => glob(__DIR__ . '/../.env.backup.*') ? 'PASS' : 'FAIL'
]
];
// Calculate overall status
$overall_status = 'PASS';
foreach ($tests as $category => $test) {
if (isset($test['status']) && $test['status'] === 'FAIL') {
$overall_status = 'FAIL';
break;
}
}
echo json_encode([
'deployment_status' => $overall_status,
'timestamp' => date('Y-m-d H:i:s'),
'note' => 'Reading production config from .env file (container env vars need rebuild)',
'tests' => $tests
], JSON_PRETTY_PRINT);
?>

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
// Enable error reporting
ini_set('display_errors', '1');
ini_set('log_errors', '1');
error_reporting(E_ALL);
echo "🧪 Framework Debug Check\n";
echo "======================\n\n";
try {
echo "1. ✅ Basic PHP OK (Version: " . PHP_VERSION . ")\n";
echo "2. Testing Autoloader...\n";
require __DIR__ . '/../vendor/autoload.php';
echo " ✅ Autoloader loaded\n";
echo "3. Testing Basic Classes...\n";
$testClasses = [
'App\\Framework\\Core\\Application',
'App\\Framework\\Core\\AppBootstrapper',
'App\\Framework\\Http\\HttpRequest',
'App\\Framework\\Http\\Method',
];
foreach ($testClasses as $class) {
if (class_exists($class)) {
echo "$class: EXISTS\n";
} else {
echo "$class: MISSING\n";
}
}
echo "4. Testing DateTime Components...\n";
$clock = new App\Framework\DateTime\SystemClock();
echo " ✅ SystemClock: " . $clock->now()->format('Y-m-d H:i:s') . "\n";
echo "5. Testing Memory Monitor...\n";
$memoryMonitor = new App\Framework\Performance\MemoryMonitor();
echo " ✅ MemoryMonitor: Current usage " . number_format(memory_get_usage(true) / 1024 / 1024, 2) . " MB\n";
echo "\n🎉 Basic framework components working!\n";
echo "The issue is likely in the Discovery System during bootstrap.\n";
} catch (Exception $e) {
echo "\n❌ ERROR: " . $e->getMessage() . "\n";
echo "Class: " . get_class($e) . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack Trace:\n" . $e->getTraceAsString() . "\n";
} catch (Error $e) {
echo "\n❌ FATAL ERROR: " . $e->getMessage() . "\n";
echo "Class: " . get_class($e) . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack Trace:\n" . $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
/**
* Minimal framework test without discovery system
*/
ini_set('display_errors', '1');
error_reporting(E_ALL);
require __DIR__ . '/../vendor/autoload.php';
try {
echo "🌐 Minimal Framework Test\n";
echo "========================\n\n";
// Test basic framework components without discovery
$pathProvider = new App\Framework\Core\PathProvider(__DIR__ . '/..');
$clock = new App\Framework\DateTime\SystemClock();
$memoryMonitor = new App\Framework\Performance\MemoryMonitor();
echo "✅ PathProvider: " . $pathProvider->getBasePath() . "\n";
echo "✅ Clock: " . $clock->now()->format('Y-m-d H:i:s') . "\n";
echo "✅ Memory: " . number_format(memory_get_usage(true) / 1024 / 1024, 2) . " MB\n";
// Test HTTP Request parsing
$method = App\Framework\Http\Method::GET;
echo "✅ HTTP Method: " . $method->value . "\n";
// Simple HTTP response
header('Content-Type: text/html; charset=utf-8');
echo "\n📦 Framework Status:\n";
echo " • Basic components: ✅ Working\n";
echo " • Discovery system: 🚧 Needs optimization\n";
echo " • Performance: ⚡ {$memoryMonitor->getPeakMemoryUsageMb()}MB peak\n";
echo "\n🎯 Next Steps:\n";
echo " 1. Optimize discovery system for large codebase\n";
echo " 2. Implement lazy loading for Framework components\n";
echo " 3. Add performance monitoring\n";
echo " 4. Test with production cache settings\n";
} catch (Throwable $e) {
echo "❌ ERROR: " . $e->getMessage() . "\n";
echo " File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo " Class: " . get_class($e) . "\n";
if (method_exists($e, 'getContext')) {
echo " Context: " . json_encode($e->getContext()) . "\n";
}
}

View File

@@ -0,0 +1,50 @@
<?php
// Security test endpoint for production deployment validation
declare(strict_types=1);
header('Content-Type: application/json');
$tests = [
'environment' => [
'app_env' => $_ENV['APP_ENV'] ?? 'not-set',
'app_debug' => $_ENV['APP_DEBUG'] ?? 'not-set',
'status' => ($_ENV['APP_ENV'] ?? '') === 'production' && ($_ENV['APP_DEBUG'] ?? '') === 'false' ? 'PASS' : 'FAIL'
],
'performance_debug' => [
'analytics_track_performance' => $_ENV['ANALYTICS_TRACK_PERFORMANCE'] ?? 'not-set',
'status' => (strpos($_ENV['ANALYTICS_TRACK_PERFORMANCE'] ?? '', 'false') !== false) ? 'PASS' : 'FAIL'
],
'xdebug' => [
'xdebug_mode' => $_ENV['XDEBUG_MODE'] ?? 'not-set',
'status' => ($_ENV['XDEBUG_MODE'] ?? '') === 'off' ? 'PASS' : 'FAIL'
],
'security_headers' => [
'https_only' => isset($_SERVER['HTTPS']) ? 'PASS' : 'FAIL',
'user_agent_required' => !empty($_SERVER['HTTP_USER_AGENT']) ? 'PASS' : 'FAIL'
]
];
// Calculate overall status
$overall_status = 'PASS';
foreach ($tests as $category => $test) {
if (isset($test['status']) && $test['status'] === 'FAIL') {
$overall_status = 'FAIL';
break;
}
if (is_array($test)) {
foreach ($test as $key => $value) {
if ($key === 'status' || !is_string($value)) continue;
if ($value === 'FAIL') {
$overall_status = 'FAIL';
break 2;
}
}
}
}
echo json_encode([
'overall_status' => $overall_status,
'timestamp' => date('Y-m-d H:i:s'),
'tests' => $tests
], JSON_PRETTY_PRINT);
?>

View File

@@ -0,0 +1,5 @@
<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(E_ALL);
echo 'DEBUG '.date('Y-m-d H:i:s');

View File

@@ -0,0 +1,253 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
// Simple debug TUI without complex dependencies
use App\Framework\Console\Components\TuiState;
use App\Framework\Console\Components\TuiInputHandler;
use App\Framework\Console\Components\TuiCommandExecutor;
use App\Framework\Console\CommandHistory;
use App\Framework\Console\TuiView;
use App\Framework\Console\TuiKeyCode;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\Screen\ScreenControlCode;
use App\Framework\Console\Screen\CursorControlCode;
echo "Simple TUI Debug Session\n";
echo "========================\n\n";
// Check TTY
if (!posix_isatty(STDIN)) {
echo "❌ Requires TTY. Run with: docker exec -it php php simple_debug_tui.php\n";
exit(1);
}
echo "✓ TTY available\n";
// Check PHPStorm
$isPhpStorm = getenv('TERMINAL_EMULATOR') === 'JetBrains-JediTerm';
echo "✓ PHPStorm detected: " . ($isPhpStorm ? "YES" : "NO") . "\n\n";
try {
// Debug command executor
$debugExecutor = new class implements TuiCommandExecutor {
public function executeSelectedCommand(object $command): void {
echo "\n🚀 EXECUTE: Selected command\n";
sleep(1);
}
public function executeCommand(string $commandName): void {
echo "\n🚀 EXECUTE: $commandName\n";
sleep(1);
}
public function validateSelectedCommand(object $command): void {
echo "\n✓ VALIDATE: Selected command\n";
sleep(1);
}
public function validateCommand(string $commandName): void {
echo "\n✓ VALIDATE: $commandName\n";
sleep(1);
}
public function showSelectedCommandHelp(object $command): void {
echo "\n📚 HELP: Selected command\n";
sleep(1);
}
public function showCommandHelp(string $commandName): void {
echo "\n📚 HELP: $commandName\n";
sleep(1);
}
public function showAllCommandsHelp(): void {
echo "\n📚 HELP: All commands\n";
sleep(1);
}
public function startInteractiveForm(object $command, \App\Framework\Console\Components\TuiState $state): void {
echo "\n📝 FORM: Start interactive form\n";
sleep(1);
}
public function findCommandObject(string $commandName): ?object {
return null;
}
};
// Custom TUI state with debug
$debugState = new class extends TuiState {
public function setSelectedCategory(int $index): void {
$oldIndex = $this->getSelectedCategory();
parent::setSelectedCategory($index);
$newIndex = $this->getSelectedCategory();
echo "\n[DEBUG] Category index: $oldIndex$newIndex\n";
flush();
}
public function navigateUp(): void {
echo "\n[DEBUG] ⬆️ navigateUp() called\n";
flush();
$before = $this->getSelectedCategory();
parent::navigateUp();
$after = $this->getSelectedCategory();
echo "[DEBUG] ⬆️ Result: $before$after\n";
flush();
}
public function navigateDown(): void {
echo "\n[DEBUG] ⬇️ navigateDown() called\n";
flush();
$before = $this->getSelectedCategory();
parent::navigateDown();
$after = $this->getSelectedCategory();
echo "[DEBUG] ⬇️ Result: $before$after\n";
flush();
}
};
// Custom input handler with debug
$debugInputHandler = new class($debugExecutor) extends TuiInputHandler {
public function handleInput(string $key, \App\Framework\Console\Components\TuiState $state, \App\Framework\Console\CommandHistory $history): void {
$keyHex = bin2hex($key);
$keyDesc = match($key) {
"\033[A" => "ARROW_UP",
"\033[B" => "ARROW_DOWN",
"\033[C" => "ARROW_RIGHT",
"\033[D" => "ARROW_LEFT",
"\n" => "ENTER",
" " => "SPACE",
"\033" => "ESC",
'q', 'Q' => "QUIT",
default => "OTHER('$key')"
};
echo "\n[INPUT] 🎯 Key: $keyDesc (hex: $keyHex)\n";
echo "[INPUT] 📍 Current view: " . $state->getCurrentView()->name . "\n";
echo "[INPUT] 📍 Current category: " . $state->getSelectedCategory() . "\n";
flush();
parent::handleInput($key, $state, $history);
echo "[INPUT] ✅ After processing - Category: " . $state->getSelectedCategory() . "\n";
flush();
}
};
$state = $debugState;
$history = new CommandHistory();
$inputHandler = $debugInputHandler;
// Add test categories
$testCategories = [
['name' => 'Testing', 'description' => 'Test commands', 'icon' => '🧪', 'commands' => []],
['name' => 'Demo', 'description' => 'Demo commands', 'icon' => '🎮', 'commands' => []],
['name' => 'Generator', 'description' => 'Code generation', 'icon' => '⚙️', 'commands' => []],
['name' => 'General', 'description' => 'General commands', 'icon' => '📂', 'commands' => []]
];
$state->setCategories($testCategories);
$state->setCurrentView(TuiView::CATEGORIES);
$state->setRunning(true);
echo "✓ Debug TUI ready\n";
echo "Categories: " . count($testCategories) . "\n";
echo "Selected: " . $state->getSelectedCategory() . "\n\n";
// Save terminal settings
$originalSettings = trim(shell_exec('stty -g') ?: '');
echo "Setting up terminal for PHPStorm...\n";
// Set up terminal
if ($isPhpStorm) {
shell_exec('stty raw -echo min 1 time 0 2>/dev/null');
} else {
shell_exec('stty -icanon -echo 2>/dev/null');
}
echo "✓ Terminal configured\n\n";
echo "🚀 SIMPLE DEBUG TUI\n";
echo "==================\n\n";
function renderSimpleMenu($categories, $selectedIndex) {
echo "Categories:\n";
foreach ($categories as $index => $category) {
$indicator = $index === $selectedIndex ? '▶️' : ' ';
echo "$indicator {$category['icon']} {$category['name']}\n";
}
echo "\n";
}
function readKeyPHPStorm() {
$key = fgetc(STDIN);
if ($key === false) return '';
if ($key === "\033") {
$sequence = $key;
stream_set_blocking(STDIN, false);
$next = fgetc(STDIN);
if ($next === false) {
usleep(10000);
$next = fgetc(STDIN);
}
if ($next !== false) {
$sequence .= $next;
if ($next === '[') {
$third = fgetc(STDIN);
if ($third === false) {
usleep(10000);
$third = fgetc(STDIN);
}
if ($third !== false) {
$sequence .= $third;
}
}
}
stream_set_blocking(STDIN, true);
return $sequence;
}
return $key;
}
// Simple debug loop
echo "Use ↑/↓ arrow keys to navigate. Press 'q' to quit.\n\n";
while ($state->isRunning()) {
// Render current state
renderSimpleMenu($testCategories, $state->getSelectedCategory());
echo "Debug Info:\n";
echo "- Selected Index: " . $state->getSelectedCategory() . "\n";
echo "- Current View: " . $state->getCurrentView()->name . "\n";
$category = $state->getCurrentCategory();
echo "- Category Name: " . ($category ? $category['name'] : 'NULL') . "\n";
echo "\nPress arrow keys...\n";
echo str_repeat('=', 40) . "\n";
// Read input
$key = readKeyPHPStorm();
if ($key === 'q' || $key === 'Q') {
echo "\n[QUIT] Stopping TUI...\n";
$state->setRunning(false);
break;
}
if ($key !== '') {
// Clear screen for next render
echo ScreenControlCode::CLEAR_ALL->format();
echo CursorControlCode::POSITION->format(1, 1);
// Process input with full debug output
$inputHandler->handleInput($key, $state, $history);
echo "\n" . str_repeat('-', 40) . "\n";
}
}
} finally {
// Restore terminal
if (!empty($originalSettings)) {
shell_exec("stty $originalSettings 2>/dev/null");
}
echo "\n✅ Simple Debug TUI ended\n";
}

View File

@@ -0,0 +1,153 @@
<?php
/**
* Minimal Hot Reload Endpoint
* Simple SSE implementation without complex framework dependencies
*/
declare(strict_types=1);
// Disable deprecation warnings
error_reporting(E_ALL & ~E_DEPRECATED);
ini_set('display_errors', 1);
// Simple .env parsing
function loadEnvVar(string $key, string $envFile = null): ?string {
$envFile ??= __DIR__ . '/../.env';
if (!file_exists($envFile)) {
return null;
}
$content = file_get_contents($envFile);
if (preg_match("/^{$key}\s*=\s*(.*)$/m", $content, $matches)) {
return trim($matches[1], " \t\n\r\0\x0B\"'");
}
return null;
}
// Only allow in development
$appDebug = loadEnvVar('APP_DEBUG');
if ($appDebug !== 'true') {
http_response_code(403);
exit('Hot Reload is only available in development mode');
}
// Set SSE headers
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no'); // Disable nginx buffering
// Get watched directories
$watchedDirs = [
__DIR__ . '/../src',
__DIR__ . '/../resources/views',
__DIR__ . '/../config',
];
// Store file modification times
$fileCache = [];
// Function to scan directories for PHP files
function scanDirectory(string $dir, array &$fileCache): array {
$changes = [];
if (!is_dir($dir)) {
return $changes;
}
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
if (!$file->isFile()) {
continue;
}
$path = $file->getPathname();
// Only watch specific file types
if (!preg_match('/\.(php|view\.php|css|js|ts)$/', $path)) {
continue;
}
$mtime = $file->getMTime();
if (!isset($fileCache[$path])) {
$fileCache[$path] = $mtime;
} elseif ($fileCache[$path] < $mtime) {
$changes[] = [
'path' => $path,
'type' => 'modified',
'time' => $mtime
];
$fileCache[$path] = $mtime;
}
}
return $changes;
}
// Send initial connection event
echo "event: connected\n";
echo "data: " . json_encode([
'status' => 'connected',
'timestamp' => date('c'),
'message' => 'Hot Reload server started'
]) . "\n\n";
flush();
// Initialize file cache
foreach ($watchedDirs as $dir) {
scanDirectory($dir, $fileCache);
}
$lastHeartbeat = time();
// Keep connection alive and watch for changes
while (connection_aborted() === 0) {
$hasChanges = false;
// Check each watched directory
foreach ($watchedDirs as $dir) {
$changes = scanDirectory($dir, $fileCache);
foreach ($changes as $change) {
$reloadType = 'full';
if (str_ends_with($change['path'], '.css')) {
$reloadType = 'css';
} elseif (str_ends_with($change['path'], '.js') || str_ends_with($change['path'], '.ts')) {
$reloadType = 'hmr';
}
echo "event: reload\n";
echo "data: " . json_encode([
'type' => $reloadType,
'file' => basename($change['path']),
'path' => $change['path'],
'timestamp' => date('c'),
'message' => 'File changed: ' . basename($change['path'])
]) . "\n\n";
flush();
$hasChanges = true;
}
}
// Send heartbeat every 30 seconds
if (time() - $lastHeartbeat >= 30) {
echo "event: heartbeat\n";
echo "data: " . json_encode([
'timestamp' => date('c'),
'message' => 'Connection alive'
]) . "\n\n";
flush();
$lastHeartbeat = time();
}
// Small delay to prevent high CPU usage
usleep(500000); // 500ms
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* Development Hot Reload Endpoint
* Bypasses full framework bootstrap to avoid deprecation warnings
*/
declare(strict_types=1);
// Disable deprecation warnings for this development endpoint
error_reporting(E_ALL & ~E_DEPRECATED);
// Set error reporting for debugging
ini_set('display_errors', 1);
error_reporting(E_ALL & ~E_DEPRECATED);
// Set up minimal autoloading
require_once __DIR__ . '/../vendor/autoload.php';
try {
// Minimal framework classes needed
use App\Framework\Filesystem\FilePath;
use App\Framework\Filesystem\FileWatcher;
use App\Framework\DateTime\SystemTimer;
// Simple .env parsing for development check
function loadEnvVar(string $key, string $envFile = null): ?string {
$envFile ??= __DIR__ . '/../.env';
if (!file_exists($envFile)) {
return null;
}
$content = file_get_contents($envFile);
if (preg_match("/^{$key}\s*=\s*(.*)$/m", $content, $matches)) {
return trim($matches[1], " \t\n\r\0\x0B\"'");
}
return null;
}
// Only allow in development
$appDebug = loadEnvVar('APP_DEBUG');
if ($appDebug !== 'true') {
http_response_code(403);
exit('Hot Reload is only available in development mode');
}
// Set SSE headers
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no'); // Disable nginx buffering
// Create file watcher
$fileWatcher = new FileWatcher(
FilePath::create(__DIR__ . '/..'),
new SystemTimer()
);
// Watch patterns
$watchPatterns = [
'src/**/*.php',
'resources/views/**/*.php',
'resources/views/**/*.view.php',
'config/**/*.php',
];
$ignorePatterns = [
'vendor/**',
'var/**',
'storage/**',
'tests/**',
'public/**',
];
// Send initial connection event
echo "event: connected\n";
echo "data: " . json_encode([
'status' => 'connected',
'timestamp' => date('c'),
'message' => 'Hot Reload server started'
]) . "\n\n";
flush();
// Keep connection alive and watch for changes
$lastCheck = time();
while (connection_aborted() === 0) {
// Check for file changes every 500ms
$changes = $fileWatcher->watchOnce($watchPatterns, $ignorePatterns);
foreach ($changes as $change) {
$reloadType = determineReloadType($change);
echo "event: reload\n";
echo "data: " . json_encode([
'type' => $reloadType->value,
'file' => $change->getPath(),
'timestamp' => $change->getTimestamp()->format('c'),
'message' => 'File changed: ' . basename($change->getPath())
]) . "\n\n";
flush();
}
// Send heartbeat every 30 seconds
if (time() - $lastCheck >= 30) {
echo "event: heartbeat\n";
echo "data: " . json_encode([
'timestamp' => date('c'),
'message' => 'Connection alive'
]) . "\n\n";
flush();
$lastCheck = time();
}
// Small delay to prevent high CPU usage
usleep(500000); // 500ms
}
function determineReloadType(FileChangeEvent $event): ReloadType
{
$path = $event->getPath();
// PHP files need full page reload
if (str_ends_with($path, '.php')) {
return ReloadType::FULL;
}
// CSS files can use hot replacement
if (str_ends_with($path, '.css')) {
return ReloadType::CSS;
}
// JS modules can use HMR if supported
if (str_ends_with($path, '.js') || str_ends_with($path, '.ts')) {
return ReloadType::HMR;
}
// Templates need full reload
if (str_ends_with($path, '.view.php')) {
return ReloadType::FULL;
}
return ReloadType::FULL;
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* Temporary workaround for the "Uninitialized string offset 0" warning in ClassLoader.php
*
* This file registers an autoloader hook that catches empty class names,
* logs them with stack traces, but doesn't throw exceptions, allowing the
* application to continue running.
*
* Usage: Include this file at the beginning of your application bootstrap process,
* before any other autoloading occurs.
*/
// Register the autoloader hook
spl_autoload_register(function($class) {
if (empty($class)) {
// Log the empty class name with stack trace
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
$logMessage = date('Y-m-d H:i:s') . " - Empty class name detected in autoloader.\nStack trace:\n" .
json_encode($trace, JSON_PRETTY_PRINT) . "\n\n";
// Log to file
file_put_contents(
__DIR__ . '/empty_class_debug.log',
$logMessage,
FILE_APPEND
);
// Also log to error_log for server logs
error_log('Empty class name detected in autoloader. See empty_class_debug.log for details.');
// Return false to continue with other autoloaders
return false;
}
// Not an empty class name, let other autoloaders handle it
return false;
}, true, true); // prepend=true to ensure this runs before other autoloaders

View File

@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
/**
* Bootstrap Discovery System
*
* This script runs the discovery scanners and stores results
* Run this ONCE to initialize the new discovery system
*/
require_once __DIR__ . '/vendor/autoload.php';
use App\Framework\BuildTime\Discovery\Scanners\AttributeScanner;
use App\Framework\BuildTime\Discovery\Scanners\InterfaceScanner;
use App\Framework\BuildTime\Discovery\Scanners\TemplateScanner;
use App\Framework\Core\PathProvider;
use App\Framework\Discovery\Storage\DiscoveryStorageService;
use App\Framework\Filesystem\FileScanner;
use App\Framework\Filesystem\FileSystemService;
use App\Framework\Logging\NullLogger;
use App\Framework\Reflection\CachedReflectionProvider;
echo "🚀 Bootstrapping Discovery System...\n\n";
$totalStart = microtime(true);
// Create dependencies
$basePath = __DIR__;
$pathProvider = new PathProvider($basePath);
$storage = new DiscoveryStorageService($pathProvider);
$fileSystemService = new FileSystemService();
$logger = null;
$fileScanner = new FileScanner($logger, null, $fileSystemService);
$reflectionProvider = new CachedReflectionProvider();
// 1. Discover Attributes
echo "📦 Discovering attributes...\n";
$attrStart = microtime(true);
$attributeScanner = new AttributeScanner($fileScanner, $reflectionProvider);
$paths = [$pathProvider->getSourcePath()];
$attributeRegistry = $attributeScanner->scan($paths);
$storage->storeAttributes($attributeRegistry);
$attrDuration = round((microtime(true) - $attrStart) * 1000, 2);
echo "{$attributeRegistry->count()} attributes in {$attrDuration}ms\n\n";
// 2. Discover Templates
echo "📄 Discovering templates...\n";
$tplStart = microtime(true);
$templateScanner = new TemplateScanner($fileScanner);
$templatePaths = [
$pathProvider->getSourcePath(),
$pathProvider->getBasePath() . '/resources'
];
$templateRegistry = $templateScanner->scan($templatePaths);
$storage->storeTemplates($templateRegistry);
$tplDuration = round((microtime(true) - $tplStart) * 1000, 2);
echo "" . count($templateRegistry->getAll()) . " templates in {$tplDuration}ms\n\n";
// 3. Discover Interfaces
echo "🔌 Discovering interface implementations...\n";
$intStart = microtime(true);
$interfaceScanner = new InterfaceScanner($fileScanner, $reflectionProvider, []);
$interfaceRegistry = $interfaceScanner->scan($paths);
$storage->storeInterfaces($interfaceRegistry);
$intDuration = round((microtime(true) - $intStart) * 1000, 2);
echo "{$interfaceRegistry->count()} implementations in {$intDuration}ms\n\n";
// Summary
$totalDuration = round((microtime(true) - $totalStart) * 1000, 2);
echo str_repeat("=", 60) . "\n";
echo "🎉 Discovery bootstrap complete in {$totalDuration}ms\n";
echo " 📁 Stored in: storage/discovery/\n";
echo str_repeat("=", 60) . "\n";

View File

@@ -0,0 +1,90 @@
<?php
// build-container.php
require __DIR__ . '/../vendor/autoload.php';
use App\Framework\DI\DefaultContainer;
use App\Framework\DI\ContainerCompiler;
// Container initialisieren
$container = new DefaultContainer();
// Hier wichtige Core-Klassen registrieren
$container->bind(\App\Framework\Http\RequestFactory::class, \App\Framework\Http\RequestFactory::class);
// Weitere Bindungen...
// Liste der zu kompilierenden Services
$services = [
\App\Framework\Core\Application::class,
\App\Framework\EventBus\DefaultEventBus::class,
\App\Framework\CommandBus\DefaultCommandBus::class,
\App\Framework\Router\HttpRouter::class,
\App\Framework\Http\RequestFactory::class,
// Weitere wichtige Services...
];
// Services aus Verzeichnissen automatisch erkennen
$servicesFromDiscovery = discoverServicesFromDirectories([
__DIR__ . '/../src/Application',
__DIR__ . '/../src/Framework',
]);
$services = array_merge($services, $servicesFromDiscovery);
// Container kompilieren
$compiler = new ContainerCompiler();
$compiler->compile(
$container,
$services,
__DIR__ . '/../cache/CompiledContainer.php'
);
echo "Container kompiliert!\n";
// Hilfsfunktion zum Entdecken von Services
function discoverServicesFromDirectories(array $directories): array
{
$services = [];
foreach ($directories as $directory) {
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
foreach ($iterator as $file) {
if ($file->isFile() && $file->getExtension() === 'php') {
$className = getClassNameFromFile($file->getRealPath());
if ($className) {
$services[] = $className;
}
}
}
}
return $services;
}
function getClassNameFromFile(string $file): ?string
{
$content = file_get_contents($file);
$tokens = token_get_all($content);
$namespace = '';
$class = '';
$namespaceFound = false;
$classFound = false;
foreach ($tokens as $token) {
if (is_array($token)) {
if ($token[0] === T_NAMESPACE) {
$namespaceFound = true;
} elseif ($namespaceFound && $token[0] === T_STRING) {
$namespace .= $token[1];
} elseif ($namespaceFound && $token[0] === T_NS_SEPARATOR) {
$namespace .= '\\';
} elseif ($token[0] === T_CLASS) {
$classFound = true;
} elseif ($classFound && $token[0] === T_STRING) {
$class = $token[1];
break;
}
} elseif ($namespaceFound && $token === ';') {
$namespaceFound = false;
}
}
return $namespace && $class ? $namespace . '\\' . $class : null;
}

View File

@@ -0,0 +1,224 @@
<?php
declare(strict_types=1);
/**
* Script to populate the database with images found in the filesystem
*
* This script scans storage/uploads/ for image files and creates database records
* to match the existing files on disk.
*/
require_once 'vendor/autoload.php';
use App\Domain\Media\Image;
use App\Framework\Core\ValueObjects\FileSize;
use App\Framework\Core\ValueObjects\Hash;
use App\Framework\Database\DatabaseManager;
use App\Framework\Filesystem\FilePath;
use App\Framework\Http\MimeType;
use App\Framework\Ulid\Ulid;
use App\Framework\Ulid\UlidGenerator;
use App\Framework\DateTime\SystemClock;
class ImageMigrationScript
{
private PDO $db;
private SystemClock $clock;
private string $uploadsPath;
public function __construct()
{
$this->clock = new SystemClock();
$this->uploadsPath = __DIR__ . '/storage/uploads';
// Initialize database
$this->initializeDatabase();
}
private function initializeDatabase(): void
{
// Simple SQLite connection for this script
$pdo = new PDO('sqlite:' . __DIR__ . '/database.sqlite');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->db = $pdo;
}
public function run(): void
{
echo "🔍 Scanning for images in: {$this->uploadsPath}\n";
if (!is_dir($this->uploadsPath)) {
echo "❌ Uploads directory not found: {$this->uploadsPath}\n";
return;
}
$imageFiles = $this->findImageFiles();
echo "📁 Found " . count($imageFiles) . " image files\n";
if (empty($imageFiles)) {
echo " No images to migrate\n";
return;
}
$this->migrateImages($imageFiles);
echo "✅ Migration completed!\n";
}
private function findImageFiles(): array
{
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($this->uploadsPath)
);
$imageFiles = [];
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
foreach ($iterator as $file) {
if (!$file->isFile()) {
continue;
}
$extension = strtolower($file->getExtension());
if (!in_array($extension, $allowedExtensions)) {
continue;
}
$imageFiles[] = [
'path' => $file->getPathname(),
'filename' => $file->getFilename(),
'extension' => $extension,
'size' => $file->getSize(),
'mtime' => $file->getMTime()
];
}
return $imageFiles;
}
private function migrateImages(array $imageFiles): void
{
// Check current database schema
$this->checkDatabaseSchema();
$migrated = 0;
$errors = 0;
foreach ($imageFiles as $fileInfo) {
try {
$this->migrateImageFile($fileInfo);
$migrated++;
echo "✓ Migrated: {$fileInfo['filename']}\n";
} catch (Exception $e) {
$errors++;
echo "❌ Error migrating {$fileInfo['filename']}: " . $e->getMessage() . "\n";
}
}
echo "\n📊 Summary:\n";
echo " Migrated: $migrated\n";
echo " Errors: $errors\n";
}
private function checkDatabaseSchema(): void
{
// Check what columns exist in the images table
$stmt = $this->db->query("PRAGMA table_info(images)");
$columns = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo "📋 Database schema (images table):\n";
foreach ($columns as $column) {
echo " - {$column['name']} ({$column['type']})\n";
}
echo "\n";
}
private function migrateImageFile(array $fileInfo): void
{
$fullPath = $fileInfo['path'];
// Extract image dimensions if possible
$imageInfo = @getimagesize($fullPath);
$width = $imageInfo[0] ?? 0;
$height = $imageInfo[1] ?? 0;
// Generate ULID
$ulidGenerator = new UlidGenerator();
$ulidString = $ulidGenerator->generate($this->clock);
// Calculate hash
$hashValue = hash_file('sha256', $fullPath);
// Determine MIME type
$mimeTypeString = match (strtolower($fileInfo['extension'])) {
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'webp' => 'image/webp',
default => 'image/jpeg'
};
// Extract original filename from the complex filename structure
$originalFilename = $this->extractOriginalFilename($fileInfo['filename']);
// Get relative path from storage root
$relativePath = str_replace($this->uploadsPath . '/', '', $fullPath);
$pathOnly = dirname($relativePath);
// Insert into database using the correct table structure
$sql = "INSERT INTO images (
ulid, filename, original_filename, mime_type, file_size,
width, height, hash, path, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $this->db->prepare($sql);
$now = date('Y-m-d H:i:s');
$stmt->execute([
$ulidString,
$fileInfo['filename'],
$originalFilename,
$mimeTypeString,
$fileInfo['size'],
$width,
$height,
$hashValue,
$pathOnly,
$now,
$now
]);
}
private function extractOriginalFilename(string $filename): string
{
// Pattern for files like: BFWCAKKEHTKF5SYR_6626fc6b...cd1_original.png
if (preg_match('/^[A-Z0-9]{16}_[a-f0-9]{64}_original\.(.+)$/', $filename, $matches)) {
// This is an original file, try to find the pattern in other files
$basePattern = substr($filename, 0, strpos($filename, '_original.'));
// For now, just return a cleaned version
return "original." . $matches[1];
}
// Pattern for simple files like: 00MF9VW9R36NJN3VCFSTS2CK6R.jpg
if (preg_match('/^[A-Z0-9]{26}\.(.+)$/', $filename, $matches)) {
return "image." . $matches[1];
}
// Fallback: return as-is
return $filename;
}
}
// Run the migration
echo "🚀 Starting image migration from filesystem to database...\n\n";
try {
$migration = new ImageMigrationScript();
$migration->run();
} catch (Exception $e) {
echo "💥 Migration failed: " . $e->getMessage() . "\n";
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
exit(1);
}
echo "\n🎉 Migration script completed!\n";

View File

@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
/**
* Quick fix to restore localhost functionality
* Clears discovery cache and forces a minimal discovery
*/
echo "🚨 Quick Fix: Restoring localhost functionality\n";
echo "===============================================\n\n";
try {
require __DIR__ . '/../vendor/autoload.php';
echo "1. ✅ Clearing discovery cache...\n";
// Clear storage cache
exec('rm -rf ' . __DIR__ . '/../storage/cache/*', $output, $returnCode);
if ($returnCode === 0) {
echo " ✅ Storage cache cleared\n";
}
// Clear any potential cache files in var/
if (is_dir(__DIR__ . '/../var/cache')) {
exec('rm -rf ' . __DIR__ . '/../var/cache/*', $output, $returnCode);
echo " ✅ Var cache cleared\n";
}
echo "2. ⚡ Optimizing discovery paths...\n";
// Create a temporary configuration to reduce discovery scope
$optimizedConfig = [
'discovery_paths' => [
'Application', // Only scan application code
'Domain' // And domain models
],
'exclude_paths' => [
'Framework/AsyncExamples', // Skip examples
'Framework/Testing', // Skip testing utilities
'Framework/Debug', // Skip debug utilities
'tests' // Skip test files
]
];
echo " ✅ Limited discovery to: " . implode(', ', $optimizedConfig['discovery_paths']) . "\n";
echo " ✅ Excluded: " . implode(', ', $optimizedConfig['exclude_paths']) . "\n";
echo "\n3. 🧪 Testing basic application...\n";
// Test if basic classes load without discovery
$testClasses = [
'App\\Framework\\Core\\Application',
'App\\Framework\\Http\\HttpRequest'
];
foreach ($testClasses as $class) {
if (class_exists($class)) {
echo "$class loaded\n";
} else {
echo "$class failed\n";
}
}
echo "\n4. 💡 Recommendations:\n";
echo " • Discovery system needs optimization for large codebase\n";
echo " • Consider implementing lazy loading for non-critical components\n";
echo " • Use incremental discovery instead of full scans\n";
echo " • Add performance monitoring to discovery process\n";
echo "\n🎉 Quick fix complete!\n";
echo "💬 Try accessing https://localhost/ now\n";
} catch (Exception $e) {
echo "❌ Error: " . $e->getMessage() . "\n";
exit(1);
}

View File

@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
echo "=== Framework Agents Status Check ===\n\n";
// Check if our FrameworkAgents class exists and has the correct MCP Tool attributes
$frameworkAgentsFile = __DIR__ . '/src/Framework/Mcp/Tools/FrameworkAgents.php';
if (file_exists($frameworkAgentsFile)) {
echo "✅ FrameworkAgents class file exists\n";
$content = file_get_contents($frameworkAgentsFile);
// Check for each agent method
$agents = [
'framework_core_agent' => 'Framework-Core Architecture Specialist Agent',
'mcp_specialist_agent' => 'MCP-Integration Specialist Agent',
'value_object_agent' => 'Value Object Specialist Agent',
'discovery_expert_agent' => 'Attribute-Discovery Specialist Agent'
];
foreach ($agents as $agentMethod => $description) {
if (strpos($content, "name: '$agentMethod'") !== false) {
echo "$agentMethod MCP tool found\n";
} else {
echo "$agentMethod MCP tool missing\n";
}
}
// Check if class has correct namespace and MCP attributes
if (strpos($content, '#[McpTool(') !== false) {
echo "✅ MCP Tool attributes detected\n";
} else {
echo "❌ No MCP Tool attributes found\n";
}
if (strpos($content, 'namespace App\Framework\Mcp\Tools;') !== false) {
echo "✅ Correct namespace found\n";
} else {
echo "❌ Incorrect namespace\n";
}
} else {
echo "❌ FrameworkAgents class file not found\n";
exit(1);
}
echo "\n=== Integration Status ===\n";
// Check if CLAUDE.md includes framework-personas.md
$claudeFile = __DIR__ . '/CLAUDE.md';
if (file_exists($claudeFile)) {
$claudeContent = file_get_contents($claudeFile);
if (strpos($claudeContent, '@docs/claude/framework-personas.md') !== false) {
echo "✅ Framework personas integrated in CLAUDE.md\n";
} else {
echo "❌ Framework personas not integrated in CLAUDE.md\n";
}
} else {
echo "❌ CLAUDE.md not found\n";
}
// Check if framework-personas.md exists
$personasFile = __DIR__ . '/docs/claude/framework-personas.md';
if (file_exists($personasFile)) {
echo "✅ Framework personas documentation exists\n";
$personasContent = file_get_contents($personasFile);
$expectedPersonas = [
'--persona-framework-core',
'--persona-mcp-specialist',
'--persona-value-object-architect',
'--persona-discovery-expert'
];
foreach ($expectedPersonas as $persona) {
if (strpos($personasContent, $persona) !== false) {
echo "$persona documented\n";
} else {
echo "$persona missing from documentation\n";
}
}
} else {
echo "❌ Framework personas documentation missing\n";
}
// Check global .claude reference
$globalReferenceFile = '/home/michael/.claude/FRAMEWORK-PERSONAS.md';
if (file_exists($globalReferenceFile)) {
echo "✅ Global Claude reference file exists\n";
$referenceContent = file_get_contents($globalReferenceFile);
if (strpos($referenceContent, '@/home/michael/dev/michaelschiemer/docs/claude/framework-personas.md') !== false) {
echo "✅ Correct reference path in global file\n";
} else {
echo "❌ Incorrect reference path in global file\n";
}
} else {
echo "❌ Global Claude reference file missing\n";
}
echo "\n=== Summary ===\n";
echo "🎉 Framework-specific agents have been successfully implemented!\n\n";
echo "📋 Available Framework Agents (as MCP Tools):\n";
foreach ($agents as $agentMethod => $description) {
echo " - $agentMethod: $description\n";
}
echo "\n💡 How to use:\n";
echo "1. The agents are automatically available when using the framework's MCP server\n";
echo "2. They will appear in Claude Code's /agents list when MCP server is properly configured\n";
echo "3. Each agent provides specialized expertise for framework-specific tasks\n";
echo "\n🔧 Next Steps:\n";
echo "1. Configure Claude Desktop to use the framework's MCP server\n";
echo "2. Test the agents via: echo '{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"id\":1}' | docker exec -i php php console.php mcp:server\n";
echo "3. Use the agents for framework-specific development tasks\n";

View File

@@ -0,0 +1,95 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
echo "Arrow Key Test\n";
echo "==============\n\n";
echo "Press arrow keys to test input reading.\n";
echo "Press 'q' to quit.\n\n";
// Enable raw mode
shell_exec('stty -icanon -echo');
function readKey(): string
{
$key = fgetc(STDIN);
if ($key === false) {
return '';
}
// Handle escape sequences (arrow keys, etc.)
if ($key === "\033") {
$sequence = $key;
// Read the next character
$next = fgetc(STDIN);
if ($next === false) {
return $key; // Just escape
}
$sequence .= $next;
// If it's a bracket, read more
if ($next === '[') {
$third = fgetc(STDIN);
if ($third !== false) {
$sequence .= $third;
// Some sequences have more characters (like Page Up/Down)
if ($third === '5' || $third === '6' || $third === '3') {
$fourth = fgetc(STDIN);
if ($fourth !== false) {
$sequence .= $fourth;
}
}
}
}
return $sequence;
}
return $key;
}
try {
while (true) {
$key = readKey();
if ($key === 'q') {
break;
}
$hexKey = bin2hex($key);
echo "Key pressed: '$key' (hex: $hexKey)\n";
switch ($key) {
case "\033[A":
echo " -> Arrow UP detected!\n";
break;
case "\033[B":
echo " -> Arrow DOWN detected!\n";
break;
case "\033[C":
echo " -> Arrow RIGHT detected!\n";
break;
case "\033[D":
echo " -> Arrow LEFT detected!\n";
break;
case "\n":
echo " -> ENTER detected!\n";
break;
case " ":
echo " -> SPACE detected!\n";
break;
case "\033":
echo " -> ESC detected!\n";
break;
default:
echo " -> Regular key: '$key'\n";
break;
}
}
} finally {
// Restore terminal
shell_exec('stty icanon echo');
echo "\nTerminal restored. Goodbye!\n";
}

View File

@@ -0,0 +1,191 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
// Final comprehensive TUI test
use App\Framework\Console\Components\TuiState;
use App\Framework\Console\Components\TuiInputHandler;
use App\Framework\Console\Components\TuiCommandExecutor;
use App\Framework\Console\Components\TuiRenderer;
use App\Framework\Console\CommandHistory;
use App\Framework\Console\CommandGroupRegistry;
use App\Framework\Console\TuiView;
use App\Framework\Console\TuiKeyCode;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\Screen\ScreenManager;
use App\Framework\Discovery\Results\DiscoveryRegistry;
echo "Final TUI Test - Complete Workflow Simulation\n";
echo "=============================================\n\n";
try {
// Create a minimal discovery registry for testing
$discoveryRegistry = new class implements \App\Framework\Discovery\Results\DiscoveryRegistry {
public function interfaces() {
return new class {
public function get(string $interface) {
return [];
}
};
}
public function attributes() {
return new class {
public function get(string $attributeClass) {
// Return empty array for ConsoleCommand attributes
return [];
}
};
}
};
// Create mock executor
$mockExecutor = new class implements \App\Framework\Console\Components\TuiCommandExecutor {
public function executeSelectedCommand(object $command): void {
echo "Mock: Execute command\n";
}
public function executeCommand(string $commandName): void {
echo "Mock: Execute command: $commandName\n";
}
public function validateSelectedCommand(object $command): void {
echo "Mock: Validate command\n";
}
public function validateCommand(string $commandName): void {
echo "Mock: Validate command: $commandName\n";
}
public function showSelectedCommandHelp(object $command): void {
echo "Mock: Show help\n";
}
public function showCommandHelp(string $commandName): void {
echo "Mock: Show help: $commandName\n";
}
public function showAllCommandsHelp(): void {
echo "Mock: Show all commands help\n";
}
public function startInteractiveForm(object $command, \App\Framework\Console\Components\TuiState $state): void {
echo "Mock: Start form\n";
}
public function findCommandObject(string $commandName): ?object {
return null;
}
};
// Create components
$screenManager = new ScreenManager(new ConsoleOutput());
$output = new ConsoleOutput();
$output->screen = $screenManager;
$state = new TuiState();
$history = new CommandHistory();
$inputHandler = new TuiInputHandler($mockExecutor);
$renderer = new TuiRenderer($output);
$groupRegistry = new CommandGroupRegistry($discoveryRegistry);
echo "✓ Components created successfully\n\n";
// Test CommandGroupRegistry::getOrganizedCommands() returns numeric array
echo "Testing CommandGroupRegistry::getOrganizedCommands():\n";
$categories = $groupRegistry->getOrganizedCommands();
echo " Categories type: " . (is_array($categories) ? "array" : gettype($categories)) . "\n";
echo " Categories count: " . count($categories) . "\n";
echo " Keys are numeric: " . (array_is_list($categories) ? "YES" : "NO") . "\n";
if (!empty($categories)) {
echo " First category structure:\n";
$firstCategory = $categories[0];
echo " - name: '{$firstCategory['name']}'\n";
echo " - description: '{$firstCategory['description']}'\n";
echo " - icon: '{$firstCategory['icon']}'\n";
echo " - commands count: " . count($firstCategory['commands']) . "\n";
}
echo "\n";
// Setup TUI state with the organized categories
$state->setCategories($categories);
$state->setCurrentView(TuiView::CATEGORIES);
$state->setRunning(true);
echo "✓ TUI State initialized:\n";
echo " Categories loaded: " . count($categories) . "\n";
echo " Current view: " . $state->getCurrentView()->name . "\n";
echo " Selected category: " . $state->getSelectedCategory() . "\n";
$currentCategory = $state->getCurrentCategory();
if ($currentCategory) {
echo " Current category name: '{$currentCategory['name']}'\n";
} else {
echo " ❌ Current category: NULL\n";
}
echo "\n";
// Test the complete navigation workflow
echo "🔍 Testing Complete Navigation Workflow:\n\n";
$maxCategoryIndex = count($categories) - 1;
if ($maxCategoryIndex >= 0) {
// Test navigation through all categories
echo "Navigation Test - Moving through all categories:\n";
// Start at first category
$state->setSelectedCategory(0);
$startCategory = $state->getCurrentCategory();
echo " Start: Category 0 => '{$startCategory['name']}'\n";
// Navigate down to last category
for ($i = 0; $i < $maxCategoryIndex; $i++) {
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
$category = $state->getCurrentCategory();
echo " Arrow DOWN: Category {$state->getSelectedCategory()} => '{$category['name']}'\n";
}
// Try to go past last category (should stay at last)
$beforeIndex = $state->getSelectedCategory();
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
$afterIndex = $state->getSelectedCategory();
echo " Boundary test (down): {$beforeIndex} => {$afterIndex} " . ($beforeIndex === $afterIndex ? "✓ PROTECTED" : "❌ FAILED") . "\n";
// Navigate back up
echo " Navigating back up...\n";
for ($i = $maxCategoryIndex; $i > 0; $i--) {
$inputHandler->handleInput(TuiKeyCode::ARROW_UP->value, $state, $history);
$category = $state->getCurrentCategory();
echo " Arrow UP: Category {$state->getSelectedCategory()} => '{$category['name']}'\n";
}
// Try to go past first category (should stay at first)
$beforeIndex = $state->getSelectedCategory();
$inputHandler->handleInput(TuiKeyCode::ARROW_UP->value, $state, $history);
$afterIndex = $state->getSelectedCategory();
echo " Boundary test (up): {$beforeIndex} => {$afterIndex} " . ($beforeIndex === $afterIndex ? "✓ PROTECTED" : "❌ FAILED") . "\n";
} else {
echo " No categories available for navigation test\n";
}
echo "\n";
// Test rendering
echo "Testing TUI Rendering:\n";
echo "======================\n";
$renderer->render($state, $history);
echo "\n";
echo "✅ FINAL TUI TEST PASSED\n";
echo "🎯 Summary:\n";
echo " ✓ CommandGroupRegistry returns numeric array\n";
echo " ✓ TuiState navigation works correctly\n";
echo " ✓ Arrow key input handling functional\n";
echo " ✓ Boundary protection working\n";
echo " ✓ TUI rendering operational\n";
echo " ✓ Welcome screen integration ready\n";
echo "\n";
echo "🚀 The TUI is now fully functional and ready for use in a real terminal!\n";
} catch (\Throwable $e) {
echo "\n❌ FINAL TUI TEST FAILED:\n";
echo "Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n" . $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use App\Framework\Core\AppBootstrapper;
use App\Framework\Mcp\Tools\FrameworkAgents;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Performance\MemoryMonitor;
try {
// Bootstrap the application (similar to console.php)
$clock = new SystemClock();
$highResClock = new SystemHighResolutionClock();
$memoryMonitor = new MemoryMonitor();
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: false);
$bootstrapper = new AppBootstrapper(__DIR__, $collector, $memoryMonitor);
$consoleApp = $bootstrapper->bootstrapConsole();
// Extract container from the ConsoleApplication via reflection
$reflection = new \ReflectionClass($consoleApp);
$containerProperty = $reflection->getProperty('container');
$containerProperty->setAccessible(true);
$container = $containerProperty->getValue($consoleApp);
// Get the FrameworkAgents instance
$frameworkAgents = $container->get(FrameworkAgents::class);
echo "=== Testing Framework Agents ===\n\n";
// Test Framework Core Agent
echo "🔧 Testing Framework Core Agent:\n";
$coreResult = $frameworkAgents->frameworkCoreAgent(
'Analyze current framework architecture and patterns',
'architecture'
);
echo "✅ Framework Core Agent Response:\n";
echo "- Agent: " . $coreResult['agent_identity'] . "\n";
echo "- Principles: " . count($coreResult['core_principles']) . " core principles\n";
echo "- Framework Health: " . $coreResult['framework_health']['status'] . "\n";
echo "- Recommendations: " . count($coreResult['recommendations']) . " recommendations\n";
echo "\n";
// Test MCP Specialist Agent
echo "🤖 Testing MCP Specialist Agent:\n";
$mcpResult = $frameworkAgents->mcpSpecialistAgent(
'Analyze MCP integration capabilities',
'tools'
);
echo "✅ MCP Specialist Agent Response:\n";
echo "- Agent: " . $mcpResult['agent_identity'] . "\n";
echo "- Principles: " . count($mcpResult['core_principles']) . " core principles\n";
echo "- Available Tools: " . count($mcpResult['mcp_framework_integration']['available_tools']) . " MCP tools\n";
echo "- Routes: " . $mcpResult['mcp_framework_integration']['routes_analysis']['total_routes'] . " total routes\n";
echo "\n";
// Test Value Object Agent
echo "💎 Testing Value Object Agent:\n";
$voResult = $frameworkAgents->valueObjectAgent(
'Analyze current value object usage and suggest improvements',
'core'
);
echo "✅ Value Object Agent Response:\n";
echo "- Agent: " . $voResult['agent_identity'] . "\n";
echo "- Principles: " . count($voResult['core_principles']) . " core principles\n";
echo "- VO Categories: " . count($voResult['value_object_categories']) . " categories\n";
echo "- Recommendations: " . count($voResult['recommendations']) . " recommendations\n";
echo "\n";
// Test Discovery Expert Agent
echo "🔍 Testing Discovery Expert Agent:\n";
$discoveryResult = $frameworkAgents->discoveryExpertAgent(
'Analyze attribute discovery system performance',
'routing'
);
echo "✅ Discovery Expert Agent Response:\n";
echo "- Agent: " . $discoveryResult['agent_identity'] . "\n";
echo "- Principles: " . count($discoveryResult['core_principles']) . " core principles\n";
echo "- Attribute Systems: " . count($discoveryResult['attribute_expertise']) . " expertise areas\n";
echo "- Components: " . count($discoveryResult['attribute_components']) . " components analyzed\n";
echo "\n";
echo "🎉 All Framework Agents are working successfully!\n";
echo "\nThese agents are now available as MCP tools:\n";
echo "- framework_core_agent\n";
echo "- mcp_specialist_agent\n";
echo "- value_object_agent\n";
echo "- discovery_expert_agent\n";
} catch (\Throwable $e) {
echo "❌ Error testing framework agents:\n";
echo "Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
exit(1);
}

View File

@@ -0,0 +1,159 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
echo "Interactive Input Test\n";
echo "=====================\n\n";
echo "This test requires a real TTY terminal.\n";
echo "Run with: docker exec -it php php test_interactive_input.php\n\n";
// Check if we have a TTY
if (!posix_isatty(STDIN)) {
echo "❌ ERROR: This script requires a TTY terminal.\n";
echo "Current environment is not a TTY.\n";
echo "Please run with: docker exec -it php php test_interactive_input.php\n";
exit(1);
}
echo "✓ TTY detected. Terminal is interactive.\n\n";
// Check stty availability
if (!function_exists('shell_exec') || shell_exec('which stty') === null) {
echo "❌ ERROR: stty command not available.\n";
echo "Raw mode setup will not work.\n";
exit(1);
}
echo "✓ stty command available.\n\n";
echo "Setting up raw mode...\n";
// Save current terminal settings
$originalSettings = trim(shell_exec('stty -g'));
echo "✓ Original terminal settings saved: $originalSettings\n";
// Set raw mode
shell_exec('stty -icanon -echo');
echo "✓ Raw mode enabled.\n\n";
echo "=== ARROW KEY TEST ===\n";
echo "Press arrow keys to test. Press 'q' to quit.\n";
echo "You should see the exact key codes being detected.\n\n";
function readKeySequence(): string {
$key = fgetc(STDIN);
if ($key === false) {
return '';
}
// Handle escape sequences
if ($key === "\033") {
$sequence = $key;
// Read next character with timeout
stream_set_blocking(STDIN, false);
$next = fgetc(STDIN);
stream_set_blocking(STDIN, true);
if ($next === false) {
return $key; // Just escape
}
$sequence .= $next;
// If it's a bracket, read more
if ($next === '[') {
stream_set_blocking(STDIN, false);
$third = fgetc(STDIN);
stream_set_blocking(STDIN, true);
if ($third !== false) {
$sequence .= $third;
// Some sequences have more characters
if (in_array($third, ['5', '6', '3', '1', '2', '4'])) {
stream_set_blocking(STDIN, false);
$fourth = fgetc(STDIN);
stream_set_blocking(STDIN, true);
if ($fourth !== false) {
$sequence .= $fourth;
}
}
}
}
return $sequence;
}
return $key;
}
try {
$testCount = 0;
while (true) {
$key = readKeySequence();
if ($key === 'q' || $key === 'Q') {
echo "\nQuitting...\n";
break;
}
if ($key !== '') {
$testCount++;
$hexKey = bin2hex($key);
$asciiKey = addcslashes($key, "\0..\37\177..\377");
echo "Test $testCount:\n";
echo " Raw: '$asciiKey'\n";
echo " Hex: $hexKey\n";
echo " Bytes: " . strlen($key) . "\n";
switch ($key) {
case "\033[A":
echo " ✓ ARROW UP detected correctly!\n";
break;
case "\033[B":
echo " ✓ ARROW DOWN detected correctly!\n";
break;
case "\033[C":
echo " ✓ ARROW RIGHT detected correctly!\n";
break;
case "\033[D":
echo " ✓ ARROW LEFT detected correctly!\n";
break;
case "\n":
case "\r":
echo " ✓ ENTER detected!\n";
break;
case " ":
echo " ✓ SPACE detected!\n";
break;
case "\033":
echo " ✓ ESC detected!\n";
break;
case "\177":
case "\x08":
echo " ✓ BACKSPACE detected!\n";
break;
default:
if (ctype_print($key)) {
echo " → Regular key: '$key'\n";
} else {
echo " → Special key (unknown)\n";
}
break;
}
echo "\n";
}
// Add small delay to prevent CPU spinning
usleep(50000); // 50ms
}
} finally {
// Always restore terminal settings
echo "Restoring terminal settings...\n";
shell_exec("stty $originalSettings");
echo "✓ Terminal restored.\n";
echo "\nTest completed. Total inputs processed: $testCount\n";
}

View File

@@ -0,0 +1,72 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
use App\Framework\SyntaxHighlighter\SyntaxHighlighter;
// Test code to highlight
$phpCode = <<<'PHP'
<?php
declare(strict_types=1);
namespace App\Example;
use App\Framework\Http\JsonResult;
#[Route(path: '/api/users', method: 'GET')]
final readonly class UserController
{
/**
* Get all users
*
* @param UserRepository $repository
* @return JsonResult<array<User>>
*/
public function getUsers(UserRepository $repository): JsonResult
{
$users = $repository->findAll();
$count = count($users);
return new JsonResult([
'users' => $users,
'count' => $count,
'message' => "Found {$count} users"
]);
}
}
PHP;
$highlighter = new SyntaxHighlighter();
// Test HTML output
echo "=== HTML Output ===\n";
$html = $highlighter->highlightWithCss($phpCode, [
'theme' => 'dark',
'lineNumbers' => true
]);
echo $html . "\n\n";
// Test console output
echo "=== Console Output ===\n";
$console = $highlighter->highlight($phpCode, 'console', [
'colorize' => true,
'lineNumbers' => true
]);
echo $console . "\n";
// Test tokenization
echo "=== Tokenization Test ===\n";
$tokens = $highlighter->tokenize($phpCode);
echo "Total tokens: " . $tokens->count() . "\n";
// Show first few tokens
$first5 = $tokens->slice(0, 5);
foreach ($first5 as $token) {
echo sprintf(
"Line %d: %s = '%s'\n",
$token->line,
$token->type->value,
trim($token->value)
);
}

View File

@@ -0,0 +1,207 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
echo "PHPStorm Terminal Compatibility Test\n";
echo "====================================\n\n";
echo "Environment Analysis:\n";
echo "- TERM: " . (getenv('TERM') ?: 'not set') . "\n";
echo "- COLORTERM: " . (getenv('COLORTERM') ?: 'not set') . "\n";
echo "- TERMINAL_EMULATOR: " . (getenv('TERMINAL_EMULATOR') ?: 'not set') . "\n";
echo "- INSIDE_EMACS: " . (getenv('INSIDE_EMACS') ?: 'not set') . "\n";
echo "- IDE_PROJECT_ROOTS: " . (getenv('IDE_PROJECT_ROOTS') ?: 'not set') . "\n";
// Check if this looks like PHPStorm/IntelliJ
$isPhpStorm = getenv('TERMINAL_EMULATOR') === 'JetBrains-JediTerm' ||
!empty(getenv('IDE_PROJECT_ROOTS')) ||
strpos(getenv('TERM') ?: '', 'jetbrains') !== false;
echo "- Detected PHPStorm: " . ($isPhpStorm ? 'YES' : 'NO') . "\n";
// TTY check
$hasTTY = posix_isatty(STDIN);
echo "- Has TTY: " . ($hasTTY ? 'YES' : 'NO') . "\n";
// STDIN properties
echo "- STDIN is resource: " . (is_resource(STDIN) ? 'YES' : 'NO') . "\n";
echo "- STDIN type: " . get_resource_type(STDIN) . "\n";
// stty availability
$hasStty = function_exists('shell_exec') && !empty(shell_exec('which stty 2>/dev/null'));
echo "- stty available: " . ($hasStty ? 'YES' : 'NO') . "\n";
if ($hasStty) {
$sttySettings = trim(shell_exec('stty -a 2>/dev/null') ?: '');
echo "- Current stty settings: " . (!empty($sttySettings) ? 'available' : 'unavailable') . "\n";
}
echo "\n";
if (!$hasTTY) {
echo "❌ No TTY available. TUI will not work in this environment.\n";
echo "Try running in a real terminal instead of PHPStorm's integrated terminal.\n";
exit(1);
}
if (!$hasStty) {
echo "❌ stty command not available. Raw mode cannot be set.\n";
exit(1);
}
echo "Setting up PHPStorm-compatible input reading...\n\n";
// Save original settings
$originalSettings = trim(shell_exec('stty -g') ?: '');
if (empty($originalSettings)) {
echo "❌ Could not save terminal settings\n";
exit(1);
}
echo "✓ Original settings saved\n";
// PHPStorm-specific terminal setup
echo "Setting up terminal for PHPStorm...\n";
// Try different approaches for PHPStorm
if ($isPhpStorm) {
echo "Using PHPStorm-optimized settings...\n";
// PHPStorm sometimes needs different settings
shell_exec('stty raw -echo min 1 time 0 2>/dev/null');
} else {
echo "Using standard settings...\n";
shell_exec('stty -icanon -echo 2>/dev/null');
}
echo "✓ Raw mode set\n\n";
echo "=== INPUT TEST ===\n";
echo "Press keys to test input. Type 'quit' to exit.\n";
echo "Pay attention to arrow key behavior.\n\n";
function readInput(): string {
$input = '';
while (true) {
$char = fgetc(STDIN);
if ($char === false) {
break;
}
$input .= $char;
// Check for complete escape sequence
if ($char === "\033") {
// Read potential escape sequence
$next = fgetc(STDIN);
if ($next !== false) {
$input .= $next;
if ($next === '[') {
$third = fgetc(STDIN);
if ($third !== false) {
$input .= $third;
// Some sequences have a 4th character
if (in_array($third, ['5', '6', '3', '1', '2', '4'])) {
$fourth = fgetc(STDIN);
if ($fourth !== false) {
$input .= $fourth;
}
}
}
}
}
break;
}
// For regular characters, break immediately
if ($char !== "\033") {
break;
}
}
return $input;
}
try {
$buffer = '';
$testCount = 0;
while (true) {
$key = readInput();
if ($key === '') {
continue;
}
$testCount++;
$buffer .= $key;
echo "Input $testCount:\n";
echo " Raw: '" . addcslashes($key, "\0..\37\177..\377") . "'\n";
echo " Hex: " . bin2hex($key) . "\n";
echo " Length: " . strlen($key) . "\n";
// Check for complete words
if (str_contains($buffer, 'quit')) {
echo "Quit command detected!\n";
break;
}
// Analyze the key
switch ($key) {
case "\033[A":
echo " ✓ ARROW UP - Perfect!\n";
break;
case "\033[B":
echo " ✓ ARROW DOWN - Perfect!\n";
break;
case "\033[C":
echo " ✓ ARROW RIGHT - Perfect!\n";
break;
case "\033[D":
echo " ✓ ARROW LEFT - Perfect!\n";
break;
case "\n":
case "\r":
echo " ✓ ENTER\n";
$buffer = ''; // Reset buffer on enter
break;
case "\033":
echo " → ESC (incomplete sequence?)\n";
break;
default:
if (ctype_print($key)) {
echo " → Character: '$key'\n";
} else {
echo " → Special/Unknown\n";
}
break;
}
echo "\n";
// Limit output to prevent spam
if ($testCount > 20) {
echo "Test limit reached. Type 'quit' to exit.\n";
}
}
} finally {
echo "\nRestoring terminal...\n";
shell_exec("stty $originalSettings 2>/dev/null");
echo "✓ Terminal restored\n";
echo "Total inputs processed: $testCount\n";
}
echo "\n=== RECOMMENDATIONS ===\n";
if ($isPhpStorm) {
echo "PHPStorm Terminal detected. Consider:\n";
echo "1. Use external terminal (Windows Terminal, iTerm2, etc.)\n";
echo "2. Or use PHPStorm's 'Terminal' tool window with different shell\n";
echo "3. Some TUI features may be limited in integrated terminals\n";
} else {
echo "Standard terminal detected. TUI should work normally.\n";
}
echo "\nIf arrow keys didn't work properly, the TUI navigation will also fail.\n";

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use App\Framework\Core\AppBootstrapper;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Performance\MemoryMonitor;
use App\Application\Campaign\PreSaveCampaign;
try {
$basePath = __DIR__;
$clock = new SystemClock();
$highResClock = new SystemHighResolutionClock();
$memoryMonitor = new MemoryMonitor();
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: true);
$bootstrapper = new AppBootstrapper($basePath, $collector, $memoryMonitor);
$app = $bootstrapper->bootstrapWeb();
$container = $app->getContainer();
echo "✅ Container bootstrapped successfully\n";
$preSave = $container->get(PreSaveCampaign::class);
echo "✅ PreSaveCampaign successfully resolved from container\n";
echo "PreSaveCampaign class: " . get_class($preSave) . "\n";
} catch (\Exception $e) {
echo "❌ Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n";
echo $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,170 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
// Test with real category structure like CommandGroupRegistry
use App\Framework\Console\Components\TuiState;
use App\Framework\Console\Components\TuiInputHandler;
use App\Framework\Console\Components\TuiCommandExecutor;
use App\Framework\Console\CommandHistory;
use App\Framework\Console\TuiView;
use App\Framework\Console\TuiKeyCode;
echo "Testing Real Navigation with CommandGroupRegistry Structure...\n";
echo "============================================================\n\n";
try {
// Create mock executor
$mockExecutor = new class implements \App\Framework\Console\Components\TuiCommandExecutor {
public function executeSelectedCommand(object $command): void {
echo "Mock: Execute command\n";
}
public function executeCommand(string $commandName): void {
echo "Mock: Execute command: $commandName\n";
}
public function validateSelectedCommand(object $command): void {
echo "Mock: Validate command\n";
}
public function validateCommand(string $commandName): void {
echo "Mock: Validate command: $commandName\n";
}
public function showSelectedCommandHelp(object $command): void {
echo "Mock: Show help\n";
}
public function showCommandHelp(string $commandName): void {
echo "Mock: Show help: $commandName\n";
}
public function showAllCommandsHelp(): void {
echo "Mock: Show all commands help\n";
}
public function startInteractiveForm(object $command, \App\Framework\Console\Components\TuiState $state): void {
echo "Mock: Start form\n";
}
public function findCommandObject(string $commandName): ?object {
return null;
}
};
$state = new TuiState();
$history = new CommandHistory();
$inputHandler = new TuiInputHandler($mockExecutor);
// Simulate the EXACT structure from CommandGroupRegistry::getOrganizedCommands()
// After uasort by priority and array_values conversion
$organizedCategories = [
'Testing' => [
'name' => 'Testing',
'description' => '',
'icon' => '🧪',
'priority' => 0,
'commands' => []
],
'Demo' => [
'name' => 'Demo',
'description' => '',
'icon' => '🎮',
'priority' => 0,
'commands' => []
],
'Generator' => [
'name' => 'Generator',
'description' => '',
'icon' => '⚙️',
'priority' => 0,
'commands' => []
],
'General' => [
'name' => 'General',
'description' => '',
'icon' => '📂',
'priority' => 0,
'commands' => []
]
];
// Sort by priority (all have priority 0 in this case, so order by keys)
uasort($organizedCategories, fn($a, $b) => $b['priority'] <=> $a['priority']);
echo "📊 Before array_values() conversion (associative array):\n";
foreach ($organizedCategories as $key => $category) {
echo " Key: '$key' => Category: '{$category['name']}'\n";
}
echo "\n";
// Convert to numeric array like our fix
$numericCategories = array_values($organizedCategories);
echo "📊 After array_values() conversion (numeric array):\n";
foreach ($numericCategories as $index => $category) {
echo " Index: $index => Category: '{$category['name']}'\n";
}
echo "\n";
// Test with the numeric array structure
$state->setCategories($numericCategories);
$state->setCurrentView(TuiView::CATEGORIES);
$state->setRunning(true);
echo "✓ Setup Complete:\n";
echo " Categories Count: " . count($numericCategories) . "\n";
echo " Current View: " . $state->getCurrentView()->name . "\n";
echo " Selected Category Index: " . $state->getSelectedCategory() . "\n";
$currentCategory = $state->getCurrentCategory();
if ($currentCategory) {
echo " Current Category Name: '{$currentCategory['name']}'\n";
} else {
echo " ❌ Current Category: NULL (This would cause navigation issues!)\n";
}
echo "\n";
// Test navigation with the real structure
echo "🔍 Testing Navigation with Real Structure:\n\n";
// Test 1: Arrow Down
echo "Test 1: Arrow DOWN\n";
$beforeCategory = $state->getCurrentCategory();
$beforeIndex = $state->getSelectedCategory();
echo " Before: Index $beforeIndex => '{$beforeCategory['name']}'\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
$afterCategory = $state->getCurrentCategory();
$afterIndex = $state->getSelectedCategory();
echo " After: Index $afterIndex => '{$afterCategory['name']}'\n";
echo " ✓ Navigation worked: " . ($beforeIndex !== $afterIndex ? "YES" : "NO") . "\n\n";
// Test 2: Arrow Down again
echo "Test 2: Arrow DOWN again\n";
$beforeCategory = $state->getCurrentCategory();
$beforeIndex = $state->getSelectedCategory();
echo " Before: Index $beforeIndex => '{$beforeCategory['name']}'\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
$afterCategory = $state->getCurrentCategory();
$afterIndex = $state->getSelectedCategory();
echo " After: Index $afterIndex => '{$afterCategory['name']}'\n";
echo " ✓ Navigation worked: " . ($beforeIndex !== $afterIndex ? "YES" : "NO") . "\n\n";
// Test 3: Arrow Up
echo "Test 3: Arrow UP\n";
$beforeCategory = $state->getCurrentCategory();
$beforeIndex = $state->getSelectedCategory();
echo " Before: Index $beforeIndex => '{$beforeCategory['name']}'\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_UP->value, $state, $history);
$afterCategory = $state->getCurrentCategory();
$afterIndex = $state->getSelectedCategory();
echo " After: Index $afterIndex => '{$afterCategory['name']}'\n";
echo " ✓ Navigation worked: " . ($beforeIndex !== $afterIndex ? "YES" : "NO") . "\n\n";
echo "✅ Real Navigation Test COMPLETED\n";
} catch (\Throwable $e) {
echo "\n❌ Real Navigation Test FAILED:\n";
echo "Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n" . $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,69 @@
<?php
require __DIR__ . '/vendor/autoload.php';
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Core\AppBootstrapper;
use App\Framework\Performance\MemoryMonitor;
echo "=== Request Container Test ===\n";
try {
$basePath = __DIR__;
$clock = new SystemClock();
$highResClock = new SystemHighResolutionClock();
$memoryMonitor = new MemoryMonitor();
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: true);
$bootstrapper = new AppBootstrapper($basePath, $collector, $memoryMonitor);
echo "1. Creating worker container...\n";
$container = $bootstrapper->bootstrapWorker();
echo "2. Container created successfully\n";
echo "3. Testing Request resolution...\n";
// Try to resolve Request interface
$request = $container->get('App\\Framework\\Http\\Request');
echo "4. ✅ SUCCESS: Request resolved as " . get_class($request) . "\n";
echo "5. Is HttpRequest instance: " . ($request instanceof \App\Framework\Http\HttpRequest ? 'YES' : 'NO') . "\n";
// Check discovery registry
echo "\n=== Discovery Registry Check ===\n";
$registry = $container->get('App\\Framework\\Discovery\\Results\\DiscoveryRegistry');
$initializers = $registry->getInitializersByClass();
echo "Total initializers: " . count($initializers) . "\n";
$requestFactoryFound = false;
foreach ($initializers as $class => $methods) {
if (str_contains($class, 'RequestFactory')) {
echo "Found RequestFactory: $class\n";
foreach ($methods as $method) {
echo " Method: $method\n";
}
$requestFactoryFound = true;
}
}
if (!$requestFactoryFound) {
echo "❌ RequestFactory not found in discovery registry!\n";
}
// Try creating request directly
echo "\n=== Direct Request Creation Test ===\n";
$requestFactory = $container->get('App\\Framework\\Http\\RequestFactory');
echo "RequestFactory resolved: " . get_class($requestFactory) . "\n";
$directRequest = $requestFactory->createFromGlobals();
echo "Direct request created: " . get_class($directRequest) . "\n";
echo "Is Request interface: " . ($directRequest instanceof \App\Framework\Http\Request ? 'YES' : 'NO') . "\n";
} catch (Throwable $e) {
echo "ERROR: " . $e->getMessage() . "\n";
echo "FILE: " . $e->getFile() . ":" . $e->getLine() . "\n";
if (str_contains($e->getMessage(), 'Cannot instantiate')) {
echo "\n=== ANALYSIS ===\n";
echo "This suggests the Discovery system did not register the RequestFactory initializer\n";
}
}

View File

@@ -0,0 +1,26 @@
<?php
require_once 'vendor/autoload.php';
use App\Framework\Http\HttpRequest;
use App\Framework\Http\Request;
use App\Framework\DI\DefaultContainer;
echo "Testing DI Container Request binding..." . PHP_EOL;
try {
// Test simple DI binding
$container = new DefaultContainer();
// Manual binding (this should work)
$container->bind(Request::class, HttpRequest::class);
echo "Binding created successfully" . PHP_EOL;
// Test resolution
$request = $container->get(Request::class);
echo "Request resolved: " . get_class($request) . PHP_EOL;
echo "Implements Request interface: " . (($request instanceof Request) ? "YES" : "NO") . PHP_EOL;
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . PHP_EOL;
echo "Trace: " . $e->getTraceAsString() . PHP_EOL;
}

View File

@@ -0,0 +1,42 @@
<?php
require __DIR__ . '/vendor/autoload.php';
// Test if the issue is specifically in web context
// by accessing localhost directly
echo "Testing localhost web request...\n";
$context = [
'http' => [
'method' => 'GET',
'header' => [
'User-Agent: Mozilla/5.0 (compatible test)',
'Host: localhost'
],
'ignore_errors' => true
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
];
$response = @file_get_contents('https://localhost/', false, stream_context_create($context));
if ($response === false) {
$error = error_get_last();
echo "Failed to fetch localhost: " . $error['message'] . "\n";
// Try HTTP instead
echo "Trying HTTP instead of HTTPS...\n";
$response = @file_get_contents('http://localhost/', false, stream_context_create($context));
if ($response === false) {
echo "HTTP also failed\n";
} else {
echo "HTTP response length: " . strlen($response) . "\n";
echo "Response snippet: " . substr($response, 0, 200) . "\n";
}
} else {
echo "Response length: " . strlen($response) . "\n";
echo "Response snippet: " . substr($response, 0, 200) . "\n";
}

View File

@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use App\Framework\Discovery\UnifiedDiscoveryService;
use App\Framework\Discovery\ValueObjects\DiscoveryOptions;
use App\Framework\Cache\Cache;
use App\Framework\Cache\CacheKey;
use App\Framework\Cache\CacheItem;
use App\Framework\Attributes\Route;
// Simple null cache for testing
$cache = new class implements Cache {
public function get(CacheKey $key): ?CacheItem { return null; }
public function set(CacheItem ...$items): bool { return true; }
public function delete(CacheKey ...$keys): bool { return true; }
public function clear(): bool { return true; }
public function has(CacheKey $key): bool { return false; }
};
try {
// Create discovery service
$discoveryService = new UnifiedDiscoveryService($cache);
// Perform discovery
$options = new DiscoveryOptions(
paths: [__DIR__ . '/src'],
cachingEnabled: false, // Disable caching for test
attributeTypes: [Route::class]
);
$registry = $discoveryService->discover($options);
$routes = $registry->getByAttribute(Route::class);
echo "=== DISCOVERED ROUTES ===\n\n";
echo "Total routes found: " . count($routes) . "\n\n";
foreach ($routes as $discovered) {
$routeAttr = $discovered->attribute->newInstance();
$className = $discovered->className;
$methodName = $discovered->methodName ?? '__invoke';
echo "Route: {$routeAttr->method->value} {$routeAttr->path}\n";
echo " Class: {$className}\n";
echo " Method: {$methodName}\n";
// Check for Campaign routes specifically
if (str_contains($className, 'Campaign')) {
echo " ⭐ CAMPAIGN ROUTE\n";
}
echo "\n";
}
// Specifically search for PreSaveCampaign
echo "\n=== SEARCHING FOR PreSaveCampaign ===\n";
$found = false;
foreach ($routes as $discovered) {
if (str_contains($discovered->className, 'PreSaveCampaign')) {
echo "✅ PreSaveCampaign FOUND!\n";
echo " Path: {$discovered->attribute->newInstance()->path}\n";
$found = true;
}
}
if (!$found) {
echo "❌ PreSaveCampaign NOT FOUND in discovery!\n";
}
} catch (\Exception $e) {
echo "❌ Error during discovery: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n";
echo $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use App\Framework\Core\ContainerBootstrapper;
use App\Framework\OAuth\Providers\SpotifyProvider;
use App\Framework\Config\Environment;
try {
$envPath = __DIR__ . '/.env';
if (!file_exists($envPath)) {
throw new \RuntimeException(".env file not found at: {$envPath}");
}
$envVars = parse_ini_file($envPath);
if ($envVars === false) {
throw new \RuntimeException("Failed to parse .env file");
}
$env = new Environment($envVars);
echo "Environment loaded\n";
echo "SPOTIFY_CLIENT_ID: " . $env->get(\App\Framework\Config\EnvKey::fromString('SPOTIFY_CLIENT_ID'), 'NOT SET') . "\n";
echo "SPOTIFY_CLIENT_SECRET: " . $env->get(\App\Framework\Config\EnvKey::fromString('SPOTIFY_CLIENT_SECRET'), 'NOT SET') . "\n";
$containerBootstrapper = new ContainerBootstrapper($env);
$container = $containerBootstrapper->bootstrap();
echo "Container bootstrapped\n";
$spotifyProvider = $container->get(SpotifyProvider::class);
echo "✅ SpotifyProvider successfully resolved from container\n";
echo "Provider name: " . $spotifyProvider->getName() . "\n";
} catch (\Exception $e) {
echo "❌ Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n";
echo $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,7 @@
<?php
echo "Testing Spotify environment variables:\n\n";
echo "SPOTIFY_CLIENT_ID: " . (getenv('SPOTIFY_CLIENT_ID') ?: 'NOT SET') . "\n";
echo "SPOTIFY_CLIENT_SECRET: " . (getenv('SPOTIFY_CLIENT_SECRET') ?: 'NOT SET') . "\n";
echo "SPOTIFY_REDIRECT_URI: " . (getenv('SPOTIFY_REDIRECT_URI') ?: 'NOT SET') . "\n";

View File

@@ -0,0 +1,140 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
// Test TUI complete functionality
use App\Framework\Console\Components\TuiState;
use App\Framework\Console\Components\TuiInputHandler;
use App\Framework\Console\Components\TuiCommandExecutor;
use App\Framework\Console\Components\TuiRenderer;
use App\Framework\Console\CommandHistory;
use App\Framework\Console\TuiView;
use App\Framework\Console\TuiKeyCode;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\Screen\ScreenManager;
echo "Testing Complete TUI Functionality...\n";
echo "=====================================\n\n";
try {
// Create mock executor
$mockExecutor = new class implements \App\Framework\Console\Components\TuiCommandExecutor {
public function executeSelectedCommand(object $command): void {
echo "Mock: Execute command\n";
}
public function executeCommand(string $commandName): void {
echo "Mock: Execute command: $commandName\n";
}
public function validateSelectedCommand(object $command): void {
echo "Mock: Validate command\n";
}
public function validateCommand(string $commandName): void {
echo "Mock: Validate command: $commandName\n";
}
public function showSelectedCommandHelp(object $command): void {
echo "Mock: Show help\n";
}
public function showCommandHelp(string $commandName): void {
echo "Mock: Show help: $commandName\n";
}
public function showAllCommandsHelp(): void {
echo "Mock: Show all commands help\n";
}
public function startInteractiveForm(object $command, \App\Framework\Console\Components\TuiState $state): void {
echo "Mock: Start form\n";
}
public function findCommandObject(string $commandName): ?object {
return null;
}
};
// Create mock output and screen manager
$screenManager = new ScreenManager(new ConsoleOutput());
$output = new ConsoleOutput();
$output->screen = $screenManager;
$state = new TuiState();
$history = new CommandHistory();
$inputHandler = new TuiInputHandler($mockExecutor);
$renderer = new TuiRenderer($output);
// Setup test categories
$categories = [
'Database' => [
'name' => 'Database',
'description' => 'Database commands',
'icon' => '🗄️',
'commands' => []
],
'Cache' => [
'name' => 'Cache',
'description' => 'Cache commands',
'icon' => '⚡',
'commands' => []
],
'Testing' => [
'name' => 'Testing',
'description' => 'Testing commands',
'icon' => '🧪',
'commands' => []
]
];
$state->setCategories($categories);
$state->setCurrentView(TuiView::CATEGORIES);
$state->setRunning(true);
echo "✅ Initial Setup Complete\n";
echo "Categories loaded: " . count($categories) . "\n";
echo "Current view: " . $state->getCurrentView()->name . "\n";
echo "Selected category: " . $state->getSelectedCategory() . "\n\n";
// Test Arrow Down Navigation
echo "Testing Arrow DOWN navigation:\n";
echo "Before: Category " . $state->getSelectedCategory() . " ('{$categories[array_keys($categories)[$state->getSelectedCategory()]]['name']}')\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
echo "After Arrow DOWN: Category " . $state->getSelectedCategory() . " ('{$categories[array_keys($categories)[$state->getSelectedCategory()]]['name']}')\n\n";
// Test Arrow Up Navigation
echo "Testing Arrow UP navigation:\n";
echo "Before: Category " . $state->getSelectedCategory() . " ('{$categories[array_keys($categories)[$state->getSelectedCategory()]]['name']}')\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_UP->value, $state, $history);
echo "After Arrow UP: Category " . $state->getSelectedCategory() . " ('{$categories[array_keys($categories)[$state->getSelectedCategory()]]['name']}')\n\n";
// Test bounds checking - try to go below 0
echo "Testing boundary protection (going below 0):\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_UP->value, $state, $history);
echo "After Arrow UP (should stay at 0): Category " . $state->getSelectedCategory() . " ('{$categories[array_keys($categories)[$state->getSelectedCategory()]]['name']}')\n\n";
// Test bounds checking - try to go above max
echo "Testing boundary protection (going above max):\n";
// Go to last item
$state->setSelectedCategory(2); // Testing category
echo "Set to last category (2): '{$categories[array_keys($categories)[$state->getSelectedCategory()]]['name']}'\n";
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
echo "After Arrow DOWN (should stay at 2): Category " . $state->getSelectedCategory() . " ('{$categories[array_keys($categories)[$state->getSelectedCategory()]]['name']}')\n\n";
// Test rendering with actual state
echo "Testing TUI Rendering:\n";
echo "=====================\n";
$state->setSelectedCategory(0);
$renderer->render($state, $history);
echo "\n✅ TUI Complete Test PASSED - All functionality works!\n";
} catch (\Throwable $e) {
echo "\n❌ TUI Complete Test FAILED:\n";
echo "Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n" . $e->getTraceAsString() . "\n";
}

View File

@@ -0,0 +1,95 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
// Test TUI navigation functionality
use App\Framework\Console\Components\TuiState;
use App\Framework\Console\Components\TuiInputHandler;
use App\Framework\Console\Components\TuiCommandExecutor;
use App\Framework\Console\CommandHistory;
use App\Framework\Console\TuiView;
use App\Framework\Console\TuiKeyCode;
echo "Testing TUI Navigation...\n";
echo "=========================\n\n";
try {
// Create mock executor
$mockExecutor = new class implements \App\Framework\Console\Components\TuiCommandExecutor {
public function executeSelectedCommand(object $command): void {
echo "Mock: Execute command\n";
}
public function executeCommand(string $commandName): void {
echo "Mock: Execute command: $commandName\n";
}
public function validateSelectedCommand(object $command): void {
echo "Mock: Validate command\n";
}
public function validateCommand(string $commandName): void {
echo "Mock: Validate command: $commandName\n";
}
public function showSelectedCommandHelp(object $command): void {
echo "Mock: Show help\n";
}
public function showCommandHelp(string $commandName): void {
echo "Mock: Show help: $commandName\n";
}
public function showAllCommandsHelp(): void {
echo "Mock: Show all commands help\n";
}
public function startInteractiveForm(object $command, \App\Framework\Console\Components\TuiState $state): void {
echo "Mock: Start form\n";
}
public function findCommandObject(string $commandName): ?object {
return null;
}
};
$state = new TuiState();
$history = new CommandHistory();
$inputHandler = new TuiInputHandler($mockExecutor);
// Setup test categories
$categories = [
'Database' => [
'name' => 'Database',
'description' => 'Database commands',
'icon' => '🗄️',
'commands' => []
],
'Cache' => [
'name' => 'Cache',
'description' => 'Cache commands',
'icon' => '⚡',
'commands' => []
]
];
$state->setCategories($categories);
$state->setCurrentView(TuiView::CATEGORIES);
$state->setRunning(true);
// Test arrow navigation
echo "Testing arrow key navigation:\n";
echo "Initial category: " . $state->getSelectedCategory() . "\n";
// Test arrow down
$inputHandler->handleInput(TuiKeyCode::ARROW_DOWN->value, $state, $history);
echo "After arrow down: " . $state->getSelectedCategory() . "\n";
// Test arrow up
$inputHandler->handleInput(TuiKeyCode::ARROW_UP->value, $state, $history);
echo "After arrow up: " . $state->getSelectedCategory() . "\n";
// Test bounds checking - try to go below 0
$inputHandler->handleInput(TuiKeyCode::ARROW_UP->value, $state, $history);
echo "After arrow up (should stay at 0): " . $state->getSelectedCategory() . "\n";
echo "\n✅ TUI Navigation Test PASSED - Arrow key handling works!\n";
} catch (\Throwable $e) {
echo "\n❌ TUI Navigation Test FAILED:\n";
echo "Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n" . $e->getTraceAsString() . "\n";
}

46
scripts/test/websocket.php Executable file
View File

@@ -0,0 +1,46 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/src/Framework/Debug/helpers.php';
use App\Framework\Core\AppBootstrapper;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Http\WebSocketServer;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\Performance\MemoryMonitor;
// Bootstrapping mit minimaler Konfiguration
$bootstrapper = new AppBootstrapper(
getcwd(),
new EnhancedPerformanceCollector(
new SystemClock(),
new SystemHighResolutionClock(),
new MemoryMonitor(),
false),
new MemoryMonitor,
);
$container = $bootstrapper->bootstrapWebSocket();
// WebSocket-Server erstellen und konfigurieren
$server = $container->get(WebSocketServer::class);
// Signal-Handler für sauberes Beenden
pcntl_signal(SIGTERM, function () use ($server) {
echo "Beende WebSocket-Server..." . PHP_EOL;
$server->stop();
exit(0);
});
pcntl_signal(SIGINT, function () use ($server) {
echo "Beende WebSocket-Server..." . PHP_EOL;
$server->stop();
exit(0);
});
// Server starten (blockierend)
echo "WebSocket-Server wird gestartet..." . PHP_EOL;
$server->start('0.0.0.0', 8080);