Protokoll #23888

ID23888
Zeitstempel2025-12-27 15:29:35.021787
Clientroot
IP
Warning: Undefined array key "request_ip" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13

Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens2,989 (Input: 0, Output: 0)
Dauer95 ms
Request-Zeit
Warning: Undefined array key "request_timestamp" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 18
Response-Zeit-

Warning: Undefined array key "error_message" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 22

Request

{
    "event": "PreToolUse",
    "tool_name": "Edit",
    "tool_input": {
        "file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/CodeAnalysis\/CodeQualityChecker.php",
        "old_string": "        \/\/ Skip config\/constant files entirely\n        if (preg_match('\/const|config|Constants\/i', $filePath)) {\n            return [];\n        }",
        "new_string": "        \/\/ Skip config\/constant files and the checker itself\n        if (preg_match('\/const|config|Constants|CodeQualityChecker\/i', $filePath)) {\n            return [];\n        }"
    }
}

Response

{
    "tool_response": {
        "filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/CodeAnalysis\/CodeQualityChecker.php",
        "oldString": "        \/\/ Skip config\/constant files entirely\n        if (preg_match('\/const|config|Constants\/i', $filePath)) {\n            return [];\n        }",
        "newString": "        \/\/ Skip config\/constant files and the checker itself\n        if (preg_match('\/const|config|Constants|CodeQualityChecker\/i', $filePath)) {\n            return [];\n        }",
        "originalFile": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\CodeAnalysis;\n\n\/\/ @responsibility: Code-Hygiene-Analyse (Score 0-100, normalisiert, gewichtet)\n\nfinal class CodeQualityChecker\n{\n    \/** @var array<string, string> Regex patterns for secret detection *\/\n    private const SECRET_PATTERNS = [\n        'password' => '\/[\"\\'](?:password|passwd|pwd)[\"\\']\\\\s*[=:]\\\\s*[\"\\'][^\"\\']{3,}[\"\\']\/i',\n        'api_key' => '\/[\"\\'](?:api[_-]?key|apikey|secret[_-]?key)[\"\\']\\\\s*[=:]\\\\s*[\"\\'][^\"\\']{8,}[\"\\']\/i',\n        'token' => '\/[\"\\'](?:token|auth[_-]?token|access[_-]?token)[\"\\']\\\\s*[=:]\\\\s*[\"\\'][^\"\\']{8,}[\"\\']\/i',\n        'url_with_creds' => '\/https?:\\\\\/\\\\\/[^\\\\s:\\\\\/]+:[^\\\\s@]+@\/i',\n    ];\n\n    \/** @var array<string, string> Regex patterns for magic number detection *\/\n    private const MAGIC_NUMBER_PATTERNS = [\n        'magic_number' => '\/(?<![\\\\w])(?:100|1000|60|24|365|3600|86400)(?![\\\\w])\/',\n    ];\n\n    \/** @var array<string, array{optimal: int, max: int}> Default hygiene thresholds *\/\n    private const HYGIENE_DEFAULTS = [\n        'loc' => ['optimal' => 200, 'max' => 500],\n        'methods' => ['optimal' => 10, 'max' => 20],\n        'classes' => ['optimal' => 1, 'max' => 3],\n        'dependencies' => ['optimal' => 5, 'max' => 15],\n        'secrets' => ['optimal' => 0, 'max' => 1],\n        'magic_numbers' => ['optimal' => 0, 'max' => 10],\n    ];\n\n    \/** @var array<string, array<string, array{optimal: int, max: int}>> File type specific modifiers *\/\n    private const FILE_TYPE_MODIFIERS = [\n        'Controller' => [\n            'loc' => ['optimal' => 300, 'max' => 500],\n            'methods' => ['optimal' => 15, 'max' => 25],\n        ],\n        'Entity' => [\n            'loc' => ['optimal' => 100, 'max' => 300],\n            'methods' => ['optimal' => 20, 'max' => 30],\n        ],\n        'Repository' => [\n            'loc' => ['optimal' => 250, 'max' => 400],\n            'dependencies' => ['optimal' => 3, 'max' => 8],\n        ],\n        'Service' => [\n            'loc' => ['optimal' => 150, 'max' => 350],\n        ],\n        'UseCase' => [\n            'loc' => ['optimal' => 150, 'max' => 350],\n        ],\n    ];\n\n    \/** @var array<string, float> Factor weights (must sum to 1.0) *\/\n    private const WEIGHTS = [\n        'dependencies' => 0.25,\n        'loc' => 0.20,\n        'methods' => 0.20,\n        'secrets' => 0.15,\n        'classes' => 0.10,\n        'magic_numbers' => 0.10,\n    ];\n\n    \/**\n     * Analysiert eine Datei und berechnet den Code Hygiene Score.\n     *\n     * @param array<string, mixed> $analysisData Daten aus code_analysis\n     * @return array{\n     *     hygiene_score: int,\n     *     factor_scores: array<string, int>,\n     *     issues_count: int,\n     *     warnings_count: int,\n     *     issues_json: string\n     * }\n     *\/\n    public function analyze(array $analysisData): array\n    {\n        $issues = [];\n        $filePath = $analysisData['file_path'] ?? '';\n        $content = $analysisData['file_content'] ?? '';\n\n        \/\/ Determine file type for modifiers\n        $fileType = $this->detectFileType($filePath);\n        $thresholds = $this->getThresholdsForType($fileType);\n\n        \/\/ Extract metrics\n        $metrics = [\n            'loc' => (int) ($analysisData['line_count'] ?? 0),\n            'methods' => $this->countMethods($analysisData),\n            'classes' => (int) ($analysisData['class_count'] ?? 1),\n            'dependencies' => $this->countDependencies($analysisData),\n            'secrets' => 0,\n            'magic_numbers' => 0,\n        ];\n\n        \/\/ Detect secrets and magic numbers\n        $secretIssues = $this->detectSecrets($content, $filePath);\n        $metrics['secrets'] = count($secretIssues);\n        $issues = array_merge($issues, $secretIssues);\n\n        $magicIssues = $this->detectMagicNumbers($content, $filePath);\n        $metrics['magic_numbers'] = count($magicIssues);\n        $issues = array_merge($issues, $magicIssues);\n\n        \/\/ Add threshold violation issues\n        $issues = array_merge($issues, $this->detectThresholdViolations($metrics, $thresholds));\n\n        \/\/ Calculate normalized factor scores\n        $factorScores = [];\n        foreach (self::WEIGHTS as $factor => $weight) {\n            $factorScores[$factor] = $this->normalize(\n                $metrics[$factor],\n                $thresholds[$factor]['optimal'],\n                $thresholds[$factor]['max']\n            );\n        }\n\n        \/\/ Calculate weighted hygiene score\n        $hygieneScore = $this->calculateWeightedScore($factorScores);\n\n        \/\/ Apply hard fail for secrets\n        if ($metrics['secrets'] > 0) {\n            $hygieneScore = min($hygieneScore, 20);\n        }\n\n        \/\/ Count warnings\n        $warningsCount = count(array_filter($issues, fn ($i) => $i['severity'] === 'warning'));\n\n        return [\n            'hygiene_score' => $hygieneScore,\n            'factor_scores' => $factorScores,\n            'issues_count' => count($issues),\n            'warnings_count' => $warningsCount,\n            'issues_json' => json_encode($issues, JSON_UNESCAPED_UNICODE),\n        ];\n    }\n\n    \/**\n     * Normalisiert einen Wert auf 0-100 Skala.\n     * Formel: max(0, 100 - ((value - optimal) \/ (max - optimal)) * 100)\n     *\/\n    private function normalize(int $value, int $optimal, int $max): int\n    {\n        if ($value <= $optimal) {\n            return 100;\n        }\n\n        if ($value >= $max) {\n            return 0;\n        }\n\n        $range = $max - $optimal;\n        if ($range === 0) {\n            return 0;\n        }\n\n        $normalized = 100 - (($value - $optimal) \/ $range) * 100;\n\n        return (int) max(0, min(100, $normalized));\n    }\n\n    \/**\n     * Berechnet den gewichteten Gesamtscore.\n     *\n     * @param array<string, int> $factorScores\n     *\/\n    private function calculateWeightedScore(array $factorScores): int\n    {\n        $weightedSum = 0.0;\n\n        foreach (self::WEIGHTS as $factor => $weight) {\n            $weightedSum += ($factorScores[$factor] ?? 0) * $weight;\n        }\n\n        return (int) round($weightedSum);\n    }\n\n    \/**\n     * Ermittelt den Dateityp aus dem Pfad.\n     *\/\n    private function detectFileType(string $filePath): ?string\n    {\n        $filename = basename($filePath);\n\n        foreach (array_keys(self::FILE_TYPE_MODIFIERS) as $type) {\n            if (str_contains($filename, $type)) {\n                return $type;\n            }\n        }\n\n        \/\/ Check directory\n        if (str_contains($filePath, '\/Controller\/')) {\n            return 'Controller';\n        }\n        if (str_contains($filePath, '\/Entity\/')) {\n            return 'Entity';\n        }\n        if (str_contains($filePath, '\/Repository\/') || str_contains($filePath, '\/Persistence\/')) {\n            return 'Repository';\n        }\n        if (str_contains($filePath, '\/Service\/')) {\n            return 'Service';\n        }\n        if (str_contains($filePath, '\/UseCase\/') || str_contains($filePath, '\/UseCases\/')) {\n            return 'UseCase';\n        }\n\n        return null;\n    }\n\n    \/**\n     * Gibt die Thresholds für einen Dateityp zurück.\n     *\n     * @return array<string, array{optimal: int, max: int}>\n     *\/\n    private function getThresholdsForType(?string $fileType): array\n    {\n        $thresholds = self::HYGIENE_DEFAULTS;\n\n        if ($fileType !== null && isset(self::FILE_TYPE_MODIFIERS[$fileType])) {\n            foreach (self::FILE_TYPE_MODIFIERS[$fileType] as $factor => $values) {\n                $thresholds[$factor] = $values;\n            }\n        }\n\n        return $thresholds;\n    }\n\n    \/**\n     * Zählt die Methoden aus den Analysedaten.\n     *\n     * @param array<string, mixed> $analysisData\n     *\/\n    private function countMethods(array $analysisData): int\n    {\n        $functions = json_decode($analysisData['functions'] ?? '[]', true);\n\n        return is_array($functions) ? count($functions) : 0;\n    }\n\n    \/**\n     * Zählt die Dependencies aus den Analysedaten.\n     *\n     * @param array<string, mixed> $analysisData\n     *\/\n    private function countDependencies(array $analysisData): int\n    {\n        $uses = json_decode($analysisData['uses'] ?? '[]', true);\n\n        return is_array($uses) ? count($uses) : 0;\n    }\n\n    \/**\n     * Erkennt Secrets im Code (Hard Fail).\n     *\n     * @return array<array{type: string, rule: string, message: string, severity: string, line?: int}>\n     *\/\n    private function detectSecrets(string $content, string $filePath): array\n    {\n        $issues = [];\n        $filename = basename($filePath);\n\n        \/\/ Skip config files\n        if (preg_match('\/^(config|\\.env|test|spec)\/i', $filename)) {\n            return [];\n        }\n\n        foreach (self::SECRET_PATTERNS as $type => $pattern) {\n            if (preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {\n                foreach ($matches[0] as $match) {\n                    $line = substr_count(substr($content, 0, $match[1]), \"\\n\") + 1;\n                    $issues[] = [\n                        'type' => 'secret',\n                        'rule' => \"hardcoded-{$type}\",\n                        'message' => \"KRITISCH: Mögliches hardcoded {$type} gefunden\",\n                        'severity' => 'critical',\n                        'line' => $line,\n                    ];\n                }\n            }\n        }\n\n        return $issues;\n    }\n\n    \/**\n     * Erkennt Magic Numbers im Code.\n     *\n     * @return array<array{type: string, rule: string, message: string, severity: string, line?: int}>\n     *\/\n    private function detectMagicNumbers(string $content, string $filePath): array\n    {\n        $issues = [];\n\n        \/\/ Skip config\/constant files entirely\n        if (preg_match('\/const|config|Constants\/i', $filePath)) {\n            return [];\n        }\n\n        $lines = explode(\"\\n\", $content);\n\n        foreach (self::MAGIC_NUMBER_PATTERNS as $type => $pattern) {\n            if (preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {\n                foreach ($matches[0] as $match) {\n       ... [TRUNCATED-26887d133f54604f]",
        "structuredPatch": [
            {
                "oldStart": 294,
                "oldLines": 8,
                "newStart": 294,
                "newLines": 8,
                "lines": [
                    "     {",
                    "         $issues = [];",
                    " ",
                    "-        \/\/ Skip config\/constant files entirely",
                    "-        if (preg_match('\/const|config|Constants\/i', $filePath)) {",
                    "+        \/\/ Skip config\/constant files and the checker itself",
                    "+        if (preg_match('\/const|config|Constants|CodeQualityChecker\/i', $filePath)) {",
                    "             return [];",
                    "         }",
                    " "
                ]
            }
        ],
        "userModified": false,
        "replaceAll": false
    }
}
← Vorheriger Zur Liste Nächster →