docs: consolidate documentation into organized structure

- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

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

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -4,11 +4,16 @@ declare(strict_types=1);
namespace App\Framework\Console;
use App\Framework\Console\Performance\ConsolePerformanceCollector;
use App\Framework\Console\Progress\ProgressMiddleware;
use App\Framework\DI\Container;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Discovery\ValueObjects\DiscoveredAttribute;
use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\FrameworkException;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use ReflectionException;
use ReflectionMethod;
use Throwable;
@@ -23,10 +28,19 @@ final readonly class CommandRegistry
/** @var array<string, DiscoveredAttribute> */
private array $discoveredAttributes;
private CommandParameterResolver $parameterResolver;
private ?ConsolePerformanceCollector $performanceCollector;
private ProgressMiddleware $progressMiddleware;
public function __construct(
private Container $container,
DiscoveryRegistry $discoveryRegistry
) {
$this->parameterResolver = new CommandParameterResolver(new MethodSignatureAnalyzer());
$this->performanceCollector = $this->createPerformanceCollector();
$this->progressMiddleware = new ProgressMiddleware();
$this->discoverCommands($discoveryRegistry);
}
@@ -51,6 +65,20 @@ final readonly class CommandRegistry
* @param array<int, string> $arguments
*/
public function executeCommand(string $commandName, array $arguments, ConsoleOutputInterface $output): ExitCode
{
if ($this->performanceCollector) {
return $this->performanceCollector->measureCommand(
$commandName,
fn () => $this->doExecuteCommand($commandName, $arguments, $output),
$arguments,
$output
);
}
return $this->doExecuteCommand($commandName, $arguments, $output);
}
private function doExecuteCommand(string $commandName, array $arguments, ConsoleOutputInterface $output): ExitCode
{
$command = $this->commandList->get($commandName);
$discoveredAttribute = $this->getDiscoveredAttribute($commandName);
@@ -60,8 +88,14 @@ final readonly class CommandRegistry
$className = $discoveredAttribute->className->getFullyQualified();
$methodName = $discoveredAttribute->methodName?->toString() ?? '__invoke';
// Get instance from container
// Performance tracking: Container resolution
$containerStart = microtime(true);
$instance = $this->container->get($className);
$containerDuration = (microtime(true) - $containerStart) * 1000;
if ($this->performanceCollector) {
$this->performanceCollector->recordContainerResolutionTime($commandName, $className, $containerDuration);
}
// Validate command structure
if (! is_object($instance) || ! method_exists($instance, $methodName)) {
@@ -75,21 +109,21 @@ final readonly class CommandRegistry
]);
}
// Create ConsoleInput
$input = new ConsoleInput($arguments, $output);
// Get reflection method for parameter resolution
$reflectionMethod = new ReflectionMethod($className, $methodName);
// Execute command
$startTime = microtime(true);
$result = $instance->$methodName($input, $output);
$executionTime = microtime(true) - $startTime;
// Performance tracking: Validation time
$validationStart = microtime(true);
$this->parameterResolver->validateMethodSignature($reflectionMethod);
$validationDuration = (microtime(true) - $validationStart) * 1000;
// Log long-running commands
if ($executionTime > 30.0) {
$output->writeLine(
sprintf("Warning: Command '%s' took %.2f seconds to execute", $commandName, $executionTime)
);
if ($this->performanceCollector) {
$this->performanceCollector->recordValidationTime($commandName, $validationDuration);
}
// Execute command with automatic parameter resolution
$result = $this->executeCommandWithReflection($instance, $reflectionMethod, $arguments, $output);
return $this->normalizeCommandResult($result);
} catch (Throwable $e) {
@@ -104,6 +138,21 @@ final readonly class CommandRegistry
}
}
private function createPerformanceCollector(): ?ConsolePerformanceCollector
{
try {
if ($this->container->has(PerformanceCollectorInterface::class)) {
$performanceCollector = $this->container->get(PerformanceCollectorInterface::class);
return new ConsolePerformanceCollector($performanceCollector);
}
} catch (Throwable $e) {
// Performance monitoring is optional - don't fail if unavailable
}
return null;
}
private function discoverCommands(DiscoveryRegistry $discoveryRegistry): void
{
$commands = [];
@@ -118,7 +167,15 @@ final readonly class CommandRegistry
} catch (Throwable $e) {
// Log warning but continue with other commands
error_log("Warning: Failed to register command from {$discoveredAttribute->className->getFullyQualified()}: {$e->getMessage()}");
if ($this->container->has(Logger::class)) {
$logger = $this->container->get(Logger::class);
$logger->warning('Failed to register command', LogContext::withData([
'class_name' => $discoveredAttribute->className->getFullyQualified(),
'error' => $e->getMessage(),
'error_class' => get_class($e),
'component' => 'CommandRegistry',
]));
}
}
}
@@ -189,4 +246,116 @@ final readonly class CommandRegistry
return ExitCode::SUCCESS;
}
/**
* Execute command with automatic parameter resolution
*/
private function executeCommandWithReflection(
object $instance,
ReflectionMethod $method,
array $arguments,
ConsoleOutputInterface $output
): ExitCode {
try {
// Create the actual command execution callback
$commandExecutor = function (ConsoleInput $input, ConsoleOutputInterface $progressAwareOutput) use ($instance, $method, $arguments) {
// Check if method uses the new reflection-based parameter style
if ($this->usesReflectionParameters($method)) {
// Performance tracking: Parameter resolution
$parameterStart = microtime(true);
$resolvedParams = $this->parameterResolver->resolveParameters($method, $arguments);
$parameterDuration = (microtime(true) - $parameterStart) * 1000;
if ($this->performanceCollector) {
$this->performanceCollector->recordParameterResolutionTime($method->getDeclaringClass()->getName(), $parameterDuration);
}
$result = $method->invokeArgs($instance, $resolvedParams);
} else {
// For legacy style, use the progress-aware output
$result = $method->invoke($instance, $input, $progressAwareOutput);
}
return $this->normalizeCommandResult($result);
};
// Wrap execution with ProgressMiddleware
$input = new ConsoleInput($arguments, $output);
return $this->progressMiddleware->handle($input, $output, $commandExecutor, $method, $instance);
} catch (\ArgumentCountError $e) {
throw new \InvalidArgumentException(
"Invalid number of arguments for command. " . $e->getMessage()
);
} catch (\TypeError $e) {
throw new \InvalidArgumentException(
"Type error in command execution: " . $e->getMessage()
);
}
}
/**
* Determine if method uses reflection-based parameters
*/
private function usesReflectionParameters(ReflectionMethod $method): bool
{
$parameters = $method->getParameters();
// If no parameters, use simple invocation (no ConsoleInput/Output needed)
if (empty($parameters)) {
return true;
}
// If first parameter is ConsoleInput, it's legacy style
$firstParam = $parameters[0];
$firstParamType = $firstParam->getType();
if ($firstParamType instanceof \ReflectionNamedType) {
$typeName = $firstParamType->getName();
if ($typeName === ConsoleInput::class || $typeName === ConsoleInputInterface::class) {
return false; // Legacy style
}
}
// If method has ConsoleInput or ConsoleOutput in parameters, it's legacy
foreach ($parameters as $param) {
$type = $param->getType();
if ($type instanceof \ReflectionNamedType) {
$typeName = $type->getName();
if (in_array($typeName, [ConsoleInput::class, ConsoleInputInterface::class, ConsoleOutputInterface::class], true)) {
return false;
}
}
}
// All other cases are considered reflection-based
return true;
}
/**
* Generate help for a specific command
*/
public function generateCommandHelp(string $commandName): string
{
$discoveredAttribute = $this->getDiscoveredAttribute($commandName);
$className = $discoveredAttribute->className->getFullyQualified();
$methodName = $discoveredAttribute->methodName?->toString() ?? '__invoke';
try {
$reflectionMethod = new ReflectionMethod($className, $methodName);
if ($this->usesReflectionParameters($reflectionMethod)) {
return $this->parameterResolver->generateMethodHelp($reflectionMethod, $commandName);
} else {
// Generate basic help for legacy commands
$command = $this->commandList->get($commandName);
return "Command: {$commandName}\nDescription: {$command->description}\n\nThis command uses legacy parameter style.";
}
} catch (ReflectionException $e) {
return "Command: {$commandName}\nError generating help: {$e->getMessage()}";
}
}
}