Backup #1030
| ID | 1030 |
| Dateipfad | /var/www/dev.campus.systemische-tools.de/src/Infrastructure/CodeAnalysis/CodeQualityChecker.php |
| Version | 3 |
| Typ |
modified |
| Größe | 7.9 KB |
| Hash | d119de8a385e88fef81d7cbed44c0aa96ded388ab7e7451b852242689d4c82c5 |
| Datum | 2025-12-25 00:21:38 |
| Geändert von | claude-code-hook |
| Grund | Claude Code Pre-Hook Backup vor Edit-Operation |
| Datei existiert |
Ja
|
Dateiinhalt
<?php
declare(strict_types=1);
namespace Infrastructure\CodeAnalysis;
// @responsibility: Code-Qualitätsanalyse (Hardcoded, Complexity, Metrics)
final class CodeQualityChecker
{
/** @var array<string, string> Regex patterns for hardcoded detection */
private const HARDCODED_PATTERNS = [
'password' => '/["\'](?:password|passwd|pwd)["\']\\s*[=:]\\s*["\'][^"\']{3,}["\']/i',
'api_key' => '/["\'](?:api[_-]?key|apikey|secret[_-]?key)["\']\\s*[=:]\\s*["\'][^"\']{8,}["\']/i',
'token' => '/["\'](?:token|auth[_-]?token|access[_-]?token)["\']\\s*[=:]\\s*["\'][^"\']{8,}["\']/i',
'ip_address' => '/["\']\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}["\']/i',
'url_with_creds' => '/https?:\\/\\/[^:]+:[^@]+@/i',
'magic_number' => '/(?<![\\w])(?:100|1000|60|24|365|3600|86400)(?![\\w])/',
];
/** @var int Minimum score for worst quality */
private const SCORE_MIN = 20;
/** @var array<string, int> Thresholds for scoring */
private const THRESHOLDS = [
'loc_excellent' => 100,
'loc_good' => 200,
'loc_acceptable' => 400,
'loc_poor' => 600,
'methods_excellent' => 5,
'methods_good' => 10,
'methods_acceptable' => 15,
'methods_poor' => 20,
'deps_excellent' => 3,
'deps_good' => 7,
'deps_acceptable' => 12,
'deps_poor' => 20,
];
/**
* Analysiert eine Datei auf Qualitätsprobleme.
*
* @param array<string, mixed> $analysisData Daten aus code_analysis
* @return array{
* complexity_score: int,
* loc_score: int,
* dependency_score: int,
* hardcoded_count: int,
* issues_count: int,
* warnings_count: int,
* quality_grade: string,
* issues_json: string
* }
*/
public function analyze(array $analysisData): array
{
$issues = [];
$warnings = [];
$filePath = $analysisData['file_path'] ?? '';
// Use stored content from DB (scan runs as root with full access)
$content = $analysisData['file_content'] ?? '';
// LOC Analysis
$lineCount = $analysisData['line_count'] ?? 0;
$locScore = $this->calculateLocScore($lineCount);
if ($lineCount > self::THRESHOLDS['loc_poor']) {
$issues[] = [
'type' => 'complexity',
'rule' => 'file-too-long',
'message' => "Datei hat {$lineCount} Zeilen (max empfohlen: " . self::THRESHOLDS['loc_acceptable'] . ")",
'severity' => 'warning',
];
}
// Method Count Analysis
$functions = json_decode($analysisData['functions'] ?? '[]', true);
$methodCount = is_array($functions) ? count($functions) : 0;
$methodScore = $this->calculateMethodScore($methodCount);
if ($methodCount > self::THRESHOLDS['methods_poor']) {
$issues[] = [
'type' => 'srp',
'rule' => 'too-many-methods',
'message' => "Klasse hat {$methodCount} Methoden (SRP-Verletzung möglich)",
'severity' => 'warning',
];
}
// Dependency Analysis
$uses = json_decode($analysisData['uses'] ?? '[]', true);
$depsCount = is_array($uses) ? count($uses) : 0;
$depScore = $this->calculateDependencyScore($depsCount);
if ($depsCount > self::THRESHOLDS['deps_poor']) {
$issues[] = [
'type' => 'coupling',
'rule' => 'too-many-dependencies',
'message' => "Klasse hat {$depsCount} Dependencies (hohe Kopplung)",
'severity' => 'warning',
];
}
// Hardcoded Detection
$hardcodedIssues = $this->detectHardcoded($content, $filePath);
$hardcodedCount = count($hardcodedIssues);
$issues = array_merge($issues, $hardcodedIssues);
// Calculate complexity score (weighted average)
$complexityScore = (int) (($locScore * 0.3 + $methodScore * 0.3 + $depScore * 0.4));
// Penalize for hardcoded values
$complexityScore = max(0, $complexityScore - ($hardcodedCount * 10));
// Determine grade
$totalIssues = count($issues);
$totalWarnings = count(array_filter($issues, fn($i) => ($i['severity'] ?? '') === 'warning'));
$grade = $this->calculateGrade($complexityScore, $totalIssues);
return [
'complexity_score' => $complexityScore,
'loc_score' => $locScore,
'dependency_score' => $depScore,
'hardcoded_count' => $hardcodedCount,
'issues_count' => $totalIssues,
'warnings_count' => $totalWarnings,
'quality_grade' => $grade,
'issues_json' => json_encode($issues, JSON_UNESCAPED_UNICODE),
];
}
private function calculateLocScore(int $loc): int
{
if ($loc <= self::THRESHOLDS['loc_excellent']) {
return 100;
}
if ($loc <= self::THRESHOLDS['loc_good']) {
return 80;
}
if ($loc <= self::THRESHOLDS['loc_acceptable']) {
return 60;
}
if ($loc <= self::THRESHOLDS['loc_poor']) {
return 40;
}
return 20;
}
private function calculateMethodScore(int $count): int
{
if ($count <= self::THRESHOLDS['methods_excellent']) {
return 100;
}
if ($count <= self::THRESHOLDS['methods_good']) {
return 80;
}
if ($count <= self::THRESHOLDS['methods_acceptable']) {
return 60;
}
if ($count <= self::THRESHOLDS['methods_poor']) {
return 40;
}
return 20;
}
private function calculateDependencyScore(int $count): int
{
if ($count <= self::THRESHOLDS['deps_excellent']) {
return 100;
}
if ($count <= self::THRESHOLDS['deps_good']) {
return 80;
}
if ($count <= self::THRESHOLDS['deps_acceptable']) {
return 60;
}
if ($count <= self::THRESHOLDS['deps_poor']) {
return 40;
}
return 20;
}
/**
* @return array<array{type: string, rule: string, message: string, severity: string, line?: int}>
*/
private function detectHardcoded(string $content, string $filePath): array
{
$issues = [];
// Skip config files and test files
$filename = basename($filePath);
if (preg_match('/^(config|\.env|test|spec)/i', $filename)) {
return [];
}
foreach (self::HARDCODED_PATTERNS as $type => $pattern) {
// Skip magic numbers in config/constant files
if ($type === 'magic_number' && preg_match('/const|config/i', $filePath)) {
continue;
}
if (preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {
foreach ($matches[0] as $match) {
$line = substr_count(substr($content, 0, $match[1]), "\n") + 1;
$issues[] = [
'type' => 'hardcoded',
'rule' => "hardcoded-{$type}",
'message' => "Möglicher hardcoded {$type}: " . substr($match[0], 0, 30) . '...',
'severity' => $type === 'magic_number' ? 'info' : 'warning',
'line' => $line,
];
}
}
}
return $issues;
}
private function calculateGrade(int $score, int $issueCount): string
{
// Penalize for issues
$adjustedScore = $score - ($issueCount * 5);
if ($adjustedScore >= 90) {
return 'A';
}
if ($adjustedScore >= 75) {
return 'B';
}
if ($adjustedScore >= 60) {
return 'C';
}
if ($adjustedScore >= 40) {
return 'D';
}
return 'F';
}
}
Vollständig herunterladen
Aktionen
Andere Versionen dieser Datei
| ID |
Version |
Typ |
Größe |
Datum |
| 1806 |
11 |
modified |
13.1 KB |
2025-12-27 15:29 |
| 1805 |
10 |
modified |
11.6 KB |
2025-12-27 15:28 |
| 1804 |
9 |
modified |
11.6 KB |
2025-12-27 15:17 |
| 1325 |
8 |
modified |
12.0 KB |
2025-12-25 16:24 |
| 1324 |
7 |
modified |
12.1 KB |
2025-12-25 16:24 |
| 1323 |
6 |
modified |
12.5 KB |
2025-12-25 16:24 |
| 1308 |
5 |
modified |
7.9 KB |
2025-12-25 16:14 |
| 1113 |
4 |
modified |
7.9 KB |
2025-12-25 09:23 |
| 1030 |
3 |
modified |
7.9 KB |
2025-12-25 00:21 |
| 1029 |
2 |
modified |
7.8 KB |
2025-12-25 00:21 |
| 964 |
1 |
modified |
7.8 KB |
2025-12-23 22:29 |
← Zurück zur Übersicht