writeLine('🐌 Slowest Console Commands', ConsoleColor::BRIGHT_YELLOW); $output->writeLine(sprintf('Threshold: %.0fms | Showing top %d', $threshold, $limit), ConsoleColor::GRAY); $output->newLine(); $consoleMetrics = $this->performanceCollector->getMetrics(PerformanceCategory::CONSOLE); $slowCommands = $this->findSlowCommands($consoleMetrics, $threshold); if (empty($slowCommands)) { $output->writeLine('🎉 No slow commands found!', ConsoleColor::GREEN); $output->writeLine(sprintf('All commands executed faster than %.0fms', $threshold), ConsoleColor::GRAY); return ExitCode::SUCCESS; } // Sort by duration (descending) usort($slowCommands, fn ($a, $b) => $b['duration'] <=> $a['duration']); $slowCommands = array_slice($slowCommands, 0, $limit); $this->displaySlowCommandsTable($output, $slowCommands); $output->newLine(); $this->displayPerformanceRecommendations($output, $slowCommands); return ExitCode::SUCCESS; } #[ConsoleCommand('perf:watch-slow', 'Monitor for slow commands in real-time')] public function watchSlowCommands(ConsoleInput $input, ConsoleOutputInterface $output, float $threshold = 5000.0): ExitCode { $output->writeLine('👀 Monitoring Slow Commands', ConsoleColor::BRIGHT_CYAN); $output->writeLine(sprintf('Threshold: %.0fms | Press Ctrl+C to stop', $threshold), ConsoleColor::GRAY); $output->newLine(); $lastMetricsCount = 0; while (true) { $consoleMetrics = $this->performanceCollector->getMetrics(PerformanceCategory::CONSOLE); $currentMetricsCount = count($consoleMetrics); // Check if new metrics were added if ($currentMetricsCount > $lastMetricsCount) { $newMetrics = array_slice($consoleMetrics, $lastMetricsCount); $this->checkForSlowCommands($output, $newMetrics, $threshold); $lastMetricsCount = $currentMetricsCount; } usleep(500000); // Wait 0.5 seconds } } #[ConsoleCommand('perf:profile-command', 'Profile a specific command execution')] public function profileCommand(ConsoleInput $input, ConsoleOutputInterface $output, string $commandName, string ...$args): ExitCode { $output->writeLine("🔍 Profiling Command: {$commandName}", ConsoleColor::BRIGHT_BLUE); $output->newLine(); // Get baseline metrics $beforeMetrics = $this->getCommandMetrics($commandName); $output->writeLine('📊 Baseline metrics captured', ConsoleColor::GRAY); $output->writeLine("Execute the command now: php console.php {$commandName} " . implode(' ', $args), ConsoleColor::YELLOW); $output->writeLine('Press Enter when command execution is complete...', ConsoleColor::GRAY); // Wait for user input fgets(STDIN); // Get after metrics $afterMetrics = $this->getCommandMetrics($commandName); $this->displayProfilingResults($output, $commandName, $beforeMetrics, $afterMetrics); return ExitCode::SUCCESS; } private function findSlowCommands(array $metrics, float $threshold): array { $slowCommands = []; foreach ($metrics as $metric) { $key = $metric->getKey(); // Look for command timing metrics if (str_contains($key, 'console_command_') && ! str_contains($key, '_error') && ! str_contains($key, '_memory')) { $duration = $metric->getTotalDuration(); if ($duration >= $threshold) { $context = $metric->getContext(); $commandName = $context['command_name'] ?? $this->extractCommandNameFromKey($key); $slowCommands[] = [ 'command' => $commandName, 'duration' => $duration, 'executions' => $metric->getCount(), 'average_duration' => $metric->getAverageDuration(), 'memory_usage' => $this->getMemoryUsageForCommand($metrics, $commandName), ]; } } } return $slowCommands; } private function displaySlowCommandsTable(ConsoleOutputInterface $output, array $slowCommands): void { $output->writeLine('┌─────────────────────────────────────────────────────────────────────┐', ConsoleColor::GRAY); $output->writeLine('│ Command │ Duration │ Executions │ Avg │ Memory │', ConsoleColor::GRAY); $output->writeLine('├─────────────────────────────────────────────────────────────────────┤', ConsoleColor::GRAY); foreach ($slowCommands as $command) { $commandName = str_pad(substr($command['command'], 0, 25), 26); $duration = str_pad(sprintf('%.0fms', $command['duration']), 8); $executions = str_pad((string) $command['executions'], 10); $average = str_pad(sprintf('%.0fms', $command['average_duration']), 6); $memory = str_pad(sprintf('%.1fMB', $command['memory_usage']), 6); $color = $command['duration'] > 10000 ? ConsoleColor::RED : ($command['duration'] > 5000 ? ConsoleColor::YELLOW : ConsoleColor::WHITE); $output->writeLine("│ {$commandName} │ {$duration} │ {$executions} │ {$average} │ {$memory} │", $color); } $output->writeLine('└─────────────────────────────────────────────────────────────────────┘', ConsoleColor::GRAY); } private function displayPerformanceRecommendations(ConsoleOutputInterface $output, array $slowCommands): void { $output->writeLine('💡 Performance Recommendations', ConsoleColor::BRIGHT_WHITE); $output->writeLine('─────────────────────────────', ConsoleColor::GRAY); $recommendations = []; foreach ($slowCommands as $command) { if ($command['duration'] > 30000) { $recommendations[] = "🔴 {$command['command']}: Consider breaking into smaller operations or adding progress indicators"; } elseif ($command['duration'] > 10000) { $recommendations[] = "🟡 {$command['command']}: Investigate for optimization opportunities"; } if ($command['memory_usage'] > 100) { $recommendations[] = "💾 {$command['command']}: High memory usage - consider streaming or batch processing"; } } if (empty($recommendations)) { $output->writeLine('All commands are performing within acceptable limits! ✅', ConsoleColor::GREEN); } else { foreach ($recommendations as $recommendation) { $output->writeLine(" {$recommendation}", ConsoleColor::WHITE); } } } private function checkForSlowCommands(ConsoleOutputInterface $output, array $newMetrics, float $threshold): void { foreach ($newMetrics as $metric) { $duration = $metric->getTotalDuration(); if ($duration >= $threshold) { $context = $metric->getContext(); $commandName = $context['command_name'] ?? 'unknown'; $timestamp = date('H:i:s'); $output->writeLine( sprintf('[%s] 🐌 Slow command detected: %s (%.0fms)', $timestamp, $commandName, $duration), ConsoleColor::RED ); } } } private function getCommandMetrics(string $commandName): array { $allMetrics = $this->performanceCollector->getMetrics(PerformanceCategory::CONSOLE); return array_filter($allMetrics, function ($metric) use ($commandName) { return str_contains($metric->getKey(), $commandName); }); } private function displayProfilingResults(ConsoleOutputInterface $output, string $commandName, array $before, array $after): void { $output->writeLine("📈 Profiling Results for: {$commandName}", ConsoleColor::BRIGHT_GREEN); $output->writeLine('───────────────────────────────────────', ConsoleColor::GRAY); $newMetrics = array_diff_key($after, $before); if (empty($newMetrics)) { $output->writeLine('No new metrics detected for this command execution.', ConsoleColor::YELLOW); return; } foreach ($newMetrics as $metric) { $output->writeLine(sprintf(' %s: %s', $metric->getKey(), $metric->getFormattedValue()), ConsoleColor::WHITE); } } private function extractCommandNameFromKey(string $key): string { if (preg_match('/console_command_(.+)/', $key, $matches)) { return $matches[1]; } return 'unknown'; } private function getMemoryUsageForCommand(array $metrics, string $commandName): float { foreach ($metrics as $metric) { if (str_contains($metric->getKey(), "console_memory_usage") && str_contains($metric->getKey(), $commandName)) { return $metric->getValue(); } } return 0.0; } }