enabled) { return $next($input, $output); } $commandName = $this->extractCommandName($input); $startTime = hrtime(true); $executedAt = new DateTimeImmutable(); try { $exitCode = $next($input, $output); } catch (\Throwable $e) { // Record the failure and re-throw $this->recordUsage( $commandName, $executedAt, $startTime, ExitCode::GENERAL_ERROR, $input ); throw $e; } $this->recordUsage($commandName, $executedAt, $startTime, $exitCode, $input); return $exitCode; } private function recordUsage( string $commandName, DateTimeImmutable $executedAt, int $startTime, ExitCode $exitCode, ConsoleInput $input ): void { try { $endTime = hrtime(true); $executionTimeNs = $endTime - $startTime; $executionTime = Duration::fromNanoseconds($executionTimeNs); $metric = CommandUsageMetric::create( commandName: $commandName, executionTime: $executionTime, exitCode: $exitCode, argumentCount: count($input->getArguments()), userId: $this->getCurrentUserId(), metadata: $this->collectMetadata($input, $exitCode) ); $this->repository->store($metric); } catch (\Throwable $e) { // Analytics collection should never break the command execution // In production, this could be logged to a separate error log error_log("Analytics collection failed: " . $e->getMessage()); } } private function extractCommandName(ConsoleInput $input): string { $arguments = $input->getArguments(); // First argument after script name is usually the command return $arguments[1] ?? 'unknown'; } private function getCurrentUserId(): ?string { // In a real implementation, this would extract user ID from context // Could be from environment variables, session data, etc. return $this->environment->getString('CONSOLE_USER_ID'); } private function collectMetadata(ConsoleInput $input, ExitCode $exitCode): array { $metadata = [ 'options' => $input->getOptions(), 'has_stdin' => ! empty(stream_get_contents(STDIN, 1)), 'php_version' => PHP_VERSION, 'memory_peak' => memory_get_peak_usage(true), 'exit_code_name' => $exitCode->name, 'environment' => $this->appConfig->type->value, ]; // Add process info if (function_exists('posix_getpid')) { $metadata['process_id'] = posix_getpid(); } return $metadata; } }