Protokoll #6814

ID6814
Zeitstempel2025-12-23 07:12:37.230312
Clientroot
IP145.224.96.190
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens8,831 (Input: 2,000, Output: 6,831)
Dauer96 ms
Request-Zeit2025-12-23 07:12:37.230312
Response-Zeit2025-12-23 07:12:37.325844

Request

{
    "event": "PreToolUse",
    "tool_name": "Edit",
    "tool_input": {
        "file_path": "\/var\/www\/dev.campus.systemische-tools.de\/docs\/plan-code-quality-gates.md",
        "old_string": "# Prüfung 4-6\n\n*Werden nach Abnahme von Prüfung 3 definiert.*",
        "new_string": "# Prüfung 4: OOP\n\n## Ziel\n\n**OOP (Object-Oriented Programming):**\n- Verhalten und Zustand sind gekoppelt\n- Objekte sind keine reinen Datencontainer\n- Keine anämischen Modelle ohne Grund\n- Kapselung wird respektiert\n\n## Grundsatz\n\nOOP-Verletzungen sind schwerer zu erkennen als strukturelle Verstöße.\nEin Objekt ist \"anämisch\" wenn es nur Getter\/Setter hat, aber keine Logik.\nDas ist semantisch. Regex kann das nicht sicher erkennen.\n\nDaher: **Hauptsächlich Post-Hook (Warnungen), wenige Pre-Hook (nur Eindeutiges).**\n\n## Abgrenzung zu anderen Prüfungen\n\n| Aspekt | Prüfung | Begründung |\n|--------|---------|------------|\n| Klassen-Größe | 1 (SRP) | Strukturelle Metrik |\n| Rollen-Einhaltung | 2 (MVC) | Controller\/Model-Trennung |\n| Typisierung | 3 (Types) | Parameter\/Return |\n| **Verhalten + Zustand** | **4 (OOP)** | Objektintegrität |\n| Duplikation | 5 (DRY) | Code-Wiederholung |\n| Abhängigkeiten | 6 (DIP) | Dependency Inversion |\n\n---\n\n## Pre-Hook (BLOCK)\n\n### P4.1: Keine public Properties in Domain\n\n**Regel:** Domain-Klassen dürfen keine public Properties haben.\n\n**Scope:** `\/Domain\/`\n\n**Pattern:**\n```python\npublic_property = re.search(r\"public\\s+(?!function|static\\s+function)\\s*\\$\\w+\", content)\n# Oder einfacher:\npublic_property = re.search(r\"public\\s+(?:readonly\\s+)?(?:\\?\\w+|\\w+)?\\s*\\$\\w+\", content)\n```\n\n**Vereinfachtes Pattern:**\n```python\n# public $var oder public Type $var oder public ?Type $var\npublic_property = re.search(r\"public\\s+(?!function|const|static\\s+function).*\\$\\w+\", content)\n```\n\n**Warum Block:**\n- Public Properties brechen Kapselung\n- Domain-Objekte müssen ihren Zustand schützen\n- Zugriff nur über Methoden\n\n**Ausnahme:**\n- `public readonly` (PHP 8.1+) ist akzeptabel\n- DTOs in `\/Infrastructure\/` dürfen public Properties haben\n\n**Verfeinertes Pattern:**\n```python\n# Block public Properties, aber erlaube public readonly\nif \"\/Domain\/\" in file_path:\n    public_props = re.findall(r\"public\\s+(?!readonly|function|const|static).*\\$\\w+\", content)\n    if public_props:\n        block(\"Public property in Domain. Use getter methods or public readonly.\")\n```\n\n---\n\n### P4.2: Keine direkten Property-Änderungen von außen (gestrichen)\n\n**Ursprüngliche Idee:** Prüfen ob Properties direkt geändert werden.\n\n**Warum gestrichen:**\n- Nicht prüfbar ohne Kontrollfluss-Analyse\n- `$obj->property = $value` vs `$this->property = $value`\n- Erfordert Wissen über Kontext\n\n**Entscheidung:** Streichen. P4.1 (keine public properties) deckt das ab.\n\n---\n\n## Post-Hook (WARN)\n\n### W4.1: Anämisches Model (Getter\/Setter-Ratio)\n\n**Regel:** Warnung wenn eine Klasse fast nur Getter\/Setter hat.\n\n**Heuristik:**\n```python\ngetters = len(re.findall(r\"public\\s+function\\s+get[A-Z]\\w*\\s*\\(\", content))\nsetters = len(re.findall(r\"public\\s+function\\s+set[A-Z]\\w*\\s*\\(\", content))\nall_public = len(re.findall(r\"public\\s+function\\s+\\w+\", content))\n\nif all_public > 0:\n    accessor_ratio = (getters + setters) \/ all_public\n    if accessor_ratio > 0.7 and all_public > 4:\n        warn(f\"Potential anemic model: {int(accessor_ratio*100)}% accessors\")\n```\n\n**Schwelle:** > 70% Getter\/Setter bei > 4 public methods\n\n**Warum Warnung:**\n- Anämische Models sind nicht immer falsch (DTOs)\n- Aber: Domain-Objekte sollten Verhalten haben\n- Menschliche Entscheidung erforderlich\n\n**Scope:** Alle Klassen, aber besonders relevant für `\/Domain\/`\n\n---\n\n### W4.2: Klasse ohne Verhalten\n\n**Regel:** Warnung wenn Klasse Properties hat aber keine echten Methoden.\n\n**Heuristik:**\n```python\nhas_properties = bool(re.search(r\"(?:private|protected|public)\\s+.*\\$\\w+\", content))\nmethods = re.findall(r\"(?:public|private|protected)\\s+function\\s+(\\w+)\", content)\nreal_methods = [m for m in methods if not m.startswith((\"get\", \"set\", \"__\"))]\n\nif has_properties and len(real_methods) == 0:\n    warn(\"Class has properties but no behavior methods\")\n```\n\n**Warum Warnung:**\n- Klassen mit nur Properties + Accessors sind verdächtig\n- Aber: Manche DTOs sind legitim\n- Kontext entscheidet\n\n---\n\n### W4.3: Fehlende Kapselung (protected\/private Ratio)\n\n**Regel:** Warnung wenn zu viele Properties public oder protected sind.\n\n**Heuristik:**\n```python\npublic_props = len(re.findall(r\"public\\s+(?!function|const).*\\$\", content))\nprotected_props = len(re.findall(r\"protected\\s+(?!function|const).*\\$\", content))\nprivate_props = len(re.findall(r\"private\\s+(?!function|const).*\\$\", content))\n\ntotal = public_props + protected_props + private_props\nif total > 0:\n    exposed_ratio = (public_props + protected_props) \/ total\n    if exposed_ratio > 0.5 and total > 3:\n        warn(f\"Low encapsulation: {int(exposed_ratio*100)}% exposed properties\")\n```\n\n**Warum Warnung:**\n- Viele protected Properties deuten auf Vererbungs-Missbrauch\n- Gute OOP bevorzugt Komposition über Vererbung\n- Aber: Nicht immer falsch\n\n---\n\n### W4.4: Zu viele statische Methoden\n\n**Regel:** Warnung bei vielen static methods.\n\n**Heuristik:**\n```python\nstatic_methods = len(re.findall(r\"public\\s+static\\s+function\", content))\nall_methods = len(re.findall(r\"public\\s+function\", content))\n\nif all_methods > 0:\n    static_ratio = static_methods \/ all_methods\n    if static_ratio > 0.5 and static_methods > 2:\n        warn(f\"High static method ratio: {static_methods}\/{all_methods}\")\n```\n\n**Warum Warnung:**\n- Statische Methoden sind prozedural, nicht OO\n- Erschweren Testing\n- Aber: Factory-Pattern, Utility-Functions legitim\n\n---\n\n## Gestrichen\n\n### Property-Änderungs-Tracking\n\n**Warum gestrichen:** Ohne Kontrollfluss-Analyse nicht möglich.\n\n### Interface-Segregation-Prüfung\n\n**Warum gestrichen:** ISP gehört zu SOLID (Prüfung 6), nicht OOP.\n\n### Inheritance-Depth-Prüfung\n\n**Ursprüngliche Idee:** Warnung bei tiefer Vererbungshierarchie.\n\n**Warum gestrichen:**\n- Erfordert Analyse über Dateigrenzen hinweg\n- Single-File-Hooks können das nicht\n- PHPStan kann das\n\n---\n\n## Zusammenfassung\n\n### Pre-Hook\n\n| ID | Prüfung | Scope | Aktion |\n|----|---------|-------|--------|\n| P4.1 | Public Property in Domain | \/Domain\/ | BLOCK |\n\n### Post-Hook\n\n| ID | Prüfung | Schwelle | Aktion |\n|----|---------|----------|--------|\n| W4.1 | Anämisches Model | > 70% Accessors, > 4 methods | WARN |\n| W4.2 | Klasse ohne Verhalten | 0 real methods | WARN |\n| W4.3 | Niedrige Kapselung | > 50% exposed, > 3 props | WARN |\n| W4.4 | Hohe Static-Ratio | > 50%, > 2 static | WARN |\n\n### Allowlist\n\n| Pfad | Grund |\n|------|-------|\n| \/vendor\/ | Third-party |\n| \/tests\/ | Test-Code |\n| \/Test\/ | PHPUnit |\n| \/Infrastructure\/DTO\/ | DTOs dürfen anämisch sein |\n\n---\n\n## Selbstkritik und Entscheidungen\n\n| Frage | Entscheidung | Begründung |\n|-------|--------------|------------|\n| Warum nur ein Pre-Hook? | OOP ist semantisch | Regex kann Verhalten nicht erkennen |\n| Warum public readonly erlauben? | PHP 8.1 Feature | Immutability ist gutes OOP |\n| DTOs in Allowlist? | Ja | DTOs sind per Definition anämisch |\n| Inheritance-Depth fehlt? | Ja, bewusst | Erfordert Cross-File-Analyse |\n\n---\n\n## OOP-Prinzipien vs. Prüfbarkeit\n\n| Prinzip | Prüfbar? | Methode |\n|---------|----------|---------|\n| Kapselung | Teilweise | Property-Visibility |\n| Verhalten+Zustand | Heuristisch | Accessor-Ratio |\n| Tell, Don't Ask | Nein | Semantisch |\n| Law of Demeter | Nein | Kontrollfluss |\n| Favor Composition | Teilweise | Static\/Inheritance |\n\n**Fazit:** OOP-Qualität ist nur indirekt messbar. Hooks erkennen Symptome, nicht Ursachen.\n\n---\n\n# Prüfung 5-6\n\n*Werden nach Abnahme von Prüfung 4 definiert.*"
    }
}

Response

{
    "tool_response": {
        "filePath": "\/var\/www\/dev.campus.systemische-tools.de\/docs\/plan-code-quality-gates.md",
        "oldString": "# Prüfung 4-6\n\n*Werden nach Abnahme von Prüfung 3 definiert.*",
        "newString": "# Prüfung 4: OOP\n\n## Ziel\n\n**OOP (Object-Oriented Programming):**\n- Verhalten und Zustand sind gekoppelt\n- Objekte sind keine reinen Datencontainer\n- Keine anämischen Modelle ohne Grund\n- Kapselung wird respektiert\n\n## Grundsatz\n\nOOP-Verletzungen sind schwerer zu erkennen als strukturelle Verstöße.\nEin Objekt ist \"anämisch\" wenn es nur Getter\/Setter hat, aber keine Logik.\nDas ist semantisch. Regex kann das nicht sicher erkennen.\n\nDaher: **Hauptsächlich Post-Hook (Warnungen), wenige Pre-Hook (nur Eindeutiges).**\n\n## Abgrenzung zu anderen Prüfungen\n\n| Aspekt | Prüfung | Begründung |\n|--------|---------|------------|\n| Klassen-Größe | 1 (SRP) | Strukturelle Metrik |\n| Rollen-Einhaltung | 2 (MVC) | Controller\/Model-Trennung |\n| Typisierung | 3 (Types) | Parameter\/Return |\n| **Verhalten + Zustand** | **4 (OOP)** | Objektintegrität |\n| Duplikation | 5 (DRY) | Code-Wiederholung |\n| Abhängigkeiten | 6 (DIP) | Dependency Inversion |\n\n---\n\n## Pre-Hook (BLOCK)\n\n### P4.1: Keine public Properties in Domain\n\n**Regel:** Domain-Klassen dürfen keine public Properties haben.\n\n**Scope:** `\/Domain\/`\n\n**Pattern:**\n```python\npublic_property = re.search(r\"public\\s+(?!function|static\\s+function)\\s*\\$\\w+\", content)\n# Oder einfacher:\npublic_property = re.search(r\"public\\s+(?:readonly\\s+)?(?:\\?\\w+|\\w+)?\\s*\\$\\w+\", content)\n```\n\n**Vereinfachtes Pattern:**\n```python\n# public $var oder public Type $var oder public ?Type $var\npublic_property = re.search(r\"public\\s+(?!function|const|static\\s+function).*\\$\\w+\", content)\n```\n\n**Warum Block:**\n- Public Properties brechen Kapselung\n- Domain-Objekte müssen ihren Zustand schützen\n- Zugriff nur über Methoden\n\n**Ausnahme:**\n- `public readonly` (PHP 8.1+) ist akzeptabel\n- DTOs in `\/Infrastructure\/` dürfen public Properties haben\n\n**Verfeinertes Pattern:**\n```python\n# Block public Properties, aber erlaube public readonly\nif \"\/Domain\/\" in file_path:\n    public_props = re.findall(r\"public\\s+(?!readonly|function|const|static).*\\$\\w+\", content)\n    if public_props:\n        block(\"Public property in Domain. Use getter methods or public readonly.\")\n```\n\n---\n\n### P4.2: Keine direkten Property-Änderungen von außen (gestrichen)\n\n**Ursprüngliche Idee:** Prüfen ob Properties direkt geändert werden.\n\n**Warum gestrichen:**\n- Nicht prüfbar ohne Kontrollfluss-Analyse\n- `$obj->property = $value` vs `$this->property = $value`\n- Erfordert Wissen über Kontext\n\n**Entscheidung:** Streichen. P4.1 (keine public properties) deckt das ab.\n\n---\n\n## Post-Hook (WARN)\n\n### W4.1: Anämisches Model (Getter\/Setter-Ratio)\n\n**Regel:** Warnung wenn eine Klasse fast nur Getter\/Setter hat.\n\n**Heuristik:**\n```python\ngetters = len(re.findall(r\"public\\s+function\\s+get[A-Z]\\w*\\s*\\(\", content))\nsetters = len(re.findall(r\"public\\s+function\\s+set[A-Z]\\w*\\s*\\(\", content))\nall_public = len(re.findall(r\"public\\s+function\\s+\\w+\", content))\n\nif all_public > 0:\n    accessor_ratio = (getters + setters) \/ all_public\n    if accessor_ratio > 0.7 and all_public > 4:\n        warn(f\"Potential anemic model: {int(accessor_ratio*100)}% accessors\")\n```\n\n**Schwelle:** > 70% Getter\/Setter bei > 4 public methods\n\n**Warum Warnung:**\n- Anämische Models sind nicht immer falsch (DTOs)\n- Aber: Domain-Objekte sollten Verhalten haben\n- Menschliche Entscheidung erforderlich\n\n**Scope:** Alle Klassen, aber besonders relevant für `\/Domain\/`\n\n---\n\n### W4.2: Klasse ohne Verhalten\n\n**Regel:** Warnung wenn Klasse Properties hat aber keine echten Methoden.\n\n**Heuristik:**\n```python\nhas_properties = bool(re.search(r\"(?:private|protected|public)\\s+.*\\$\\w+\", content))\nmethods = re.findall(r\"(?:public|private|protected)\\s+function\\s+(\\w+)\", content)\nreal_methods = [m for m in methods if not m.startswith((\"get\", \"set\", \"__\"))]\n\nif has_properties and len(real_methods) == 0:\n    warn(\"Class has properties but no behavior methods\")\n```\n\n**Warum Warnung:**\n- Klassen mit nur Properties + Accessors sind verdächtig\n- Aber: Manche DTOs sind legitim\n- Kontext entscheidet\n\n---\n\n### W4.3: Fehlende Kapselung (protected\/private Ratio)\n\n**Regel:** Warnung wenn zu viele Properties public oder protected sind.\n\n**Heuristik:**\n```python\npublic_props = len(re.findall(r\"public\\s+(?!function|const).*\\$\", content))\nprotected_props = len(re.findall(r\"protected\\s+(?!function|const).*\\$\", content))\nprivate_props = len(re.findall(r\"private\\s+(?!function|const).*\\$\", content))\n\ntotal = public_props + protected_props + private_props\nif total > 0:\n    exposed_ratio = (public_props + protected_props) \/ total\n    if exposed_ratio > 0.5 and total > 3:\n        warn(f\"Low encapsulation: {int(exposed_ratio*100)}% exposed properties\")\n```\n\n**Warum Warnung:**\n- Viele protected Properties deuten auf Vererbungs-Missbrauch\n- Gute OOP bevorzugt Komposition über Vererbung\n- Aber: Nicht immer falsch\n\n---\n\n### W4.4: Zu viele statische Methoden\n\n**Regel:** Warnung bei vielen static methods.\n\n**Heuristik:**\n```python\nstatic_methods = len(re.findall(r\"public\\s+static\\s+function\", content))\nall_methods = len(re.findall(r\"public\\s+function\", content))\n\nif all_methods > 0:\n    static_ratio = static_methods \/ all_methods\n    if static_ratio > 0.5 and static_methods > 2:\n        warn(f\"High static method ratio: {static_methods}\/{all_methods}\")\n```\n\n**Warum Warnung:**\n- Statische Methoden sind prozedural, nicht OO\n- Erschweren Testing\n- Aber: Factory-Pattern, Utility-Functions legitim\n\n---\n\n## Gestrichen\n\n### Property-Änderungs-Tracking\n\n**Warum gestrichen:** Ohne Kontrollfluss-Analyse nicht möglich.\n\n### Interface-Segregation-Prüfung\n\n**Warum gestrichen:** ISP gehört zu SOLID (Prüfung 6), nicht OOP.\n\n### Inheritance-Depth-Prüfung\n\n**Ursprüngliche Idee:** Warnung bei tiefer Vererbungshierarchie.\n\n**Warum gestrichen:**\n- Erfordert Analyse über Dateigrenzen hinweg\n- Single-File-Hooks können das nicht\n- PHPStan kann das\n\n---\n\n## Zusammenfassung\n\n### Pre-Hook\n\n| ID | Prüfung | Scope | Aktion |\n|----|---------|-------|--------|\n| P4.1 | Public Property in Domain | \/Domain\/ | BLOCK |\n\n### Post-Hook\n\n| ID | Prüfung | Schwelle | Aktion |\n|----|---------|----------|--------|\n| W4.1 | Anämisches Model | > 70% Accessors, > 4 methods | WARN |\n| W4.2 | Klasse ohne Verhalten | 0 real methods | WARN |\n| W4.3 | Niedrige Kapselung | > 50% exposed, > 3 props | WARN |\n| W4.4 | Hohe Static-Ratio | > 50%, > 2 static | WARN |\n\n### Allowlist\n\n| Pfad | Grund |\n|------|-------|\n| \/vendor\/ | Third-party |\n| \/tests\/ | Test-Code |\n| \/Test\/ | PHPUnit |\n| \/Infrastructure\/DTO\/ | DTOs dürfen anämisch sein |\n\n---\n\n## Selbstkritik und Entscheidungen\n\n| Frage | Entscheidung | Begründung |\n|-------|--------------|------------|\n| Warum nur ein Pre-Hook? | OOP ist semantisch | Regex kann Verhalten nicht erkennen |\n| Warum public readonly erlauben? | PHP 8.1 Feature | Immutability ist gutes OOP |\n| DTOs in Allowlist? | Ja | DTOs sind per Definition anämisch |\n| Inheritance-Depth fehlt? | Ja, bewusst | Erfordert Cross-File-Analyse |\n\n---\n\n## OOP-Prinzipien vs. Prüfbarkeit\n\n| Prinzip | Prüfbar? | Methode |\n|---------|----------|---------|\n| Kapselung | Teilweise | Property-Visibility |\n| Verhalten+Zustand | Heuristisch | Accessor-Ratio |\n| Tell, Don't Ask | Nein | Semantisch |\n| Law of Demeter | Nein | Kontrollfluss |\n| Favor Composition | Teilweise | Static\/Inheritance |\n\n**Fazit:** OOP-Qualität ist nur indirekt messbar. Hooks erkennen Symptome, nicht Ursachen.\n\n---\n\n# Prüfung 5-6\n\n*Werden nach Abnahme von Prüfung 4 definiert.*",
        "originalFile": "# Code Quality Gates\n\n## Prüfungsreihenfolge\n\n| # | Prinzip | Begründung der Reihenfolge |\n|---|---------|---------------------------|\n| 1 | SRP + KISS | Grundlage für alles. Ohne klare Verantwortung ist nichts anderes prüfbar. |\n| 2 | MVC + CRUD | Strukturelle Trennung. Setzt SRP voraus. |\n| 3 | PSR + Types | Typdisziplin. Muss VOR DIP kommen, weil Interfaces ohne Typen wirkungslos sind. |\n| 4 | OOP | Verhalten + Zustand. Setzt Typen voraus. |\n| 5 | DRY | Duplikationserkennung. Setzt funktionierende Struktur voraus. |\n| 6 | SOLID + DIP | Abhängigkeitsrichtung. Setzt Typen und Interfaces voraus. Kommt zuletzt. |\n\n---\n\n# Prüfung 1: SRP + KISS\n\n## Ziel\n\n**SRP:** Eine Klasse hat genau einen Grund sich zu ändern.\n\n**KISS:** Einfachste Lösung die funktioniert. Keine vorzeitigen Abstraktionen.\n\n## Grundsatz\n\nSRP ist semantisch. Maschinen können \"Verantwortung\" nicht verstehen.\n\nDaher:\n- **Pre-Hook:** Erzwingt explizite Deklaration + blockiert eindeutige Symptome\n- **Post-Hook:** Warnt bei statistischen Indikatoren\n\n---\n\n## Pre-Hook (BLOCK)\n\n### P1.1: Verantwortungs-Header\n\n**Regel:** Jede PHP-Datei muss ihre Verantwortung explizit deklarieren.\n\n**Format:**\n```php\n<?php\ndeclare(strict_types=1);\n\n\/\/ @responsibility: <genau-eine-verantwortung>\n```\n\n**Validierung:**\n\n| Check | Begründung |\n|-------|------------|\n| Header vorhanden | Ohne Deklaration entsteht Scope Creep |\n| Nicht leer | Leerer Header ist wertlos |\n| Keine Multi-Responsibility-Wörter | \"und\", \"sowie\", \"außerdem\", \"zusätzlich\" im Header = mehrere Verantwortungen |\n\n**Pattern:**\n```python\nheader_pattern = r\"\/\/\\s*@responsibility:\\s*(.+)\"\nmulti_words = r\"\\b(und|sowie|außerdem|also|zusätzlich)\\b\"\n\nmatch = re.search(header_pattern, content)\nif not match:\n    block(\"Missing @responsibility header\")\n\nresponsibility_text = match.group(1)\nif re.search(multi_words, responsibility_text, re.IGNORECASE):\n    block(\"@responsibility contains multi-responsibility indicator\")\n```\n\n**Warum diese Wörter:**\n- \"Validiert Benutzer und erstellt Sessions\" = 2 Verantwortungen\n- \"Lädt Daten sowie formatiert Ausgabe\" = 2 Verantwortungen\n- Erzwingt Nachdenken über Singular-Verantwortung\n\n**Allowlist:** `\/vendor\/`, `\/tests\/`, `\/Test\/`\n\n---\n\n### P1.2: Müllhalden-Namen\n\n**Regel:** Bestimmte Begriffe im Dateinamen sind immer Symptome für fehlende klare Verantwortung.\n\n**Blockiert (case-insensitive):**\n\n| Begriff | Begründung |\n|---------|------------|\n| Helper | \"Hilft bei allem\" = keine klare Verantwortung |\n| Utils | Sammelbecken für Funktionen ohne Heimat |\n| Common | \"Wird überall gebraucht\" = gehört nirgends hin |\n| Misc | Explizit: \"Verschiedenes\" |\n| Base | Oft Gott-Klasse mit Shared-State |\n\n**Nicht blockiert:**\n- \"Manager\" → Kann legitim sein (TransactionManager, EntityManager)\n- \"And\" im Namen → Sprachlich, nicht strukturell\n\n**Pattern:**\n```python\nforbidden = [\"helper\", \"utils\", \"common\", \"misc\", \"base\"]\nfilename_lower = filename.lower()\nfor term in forbidden:\n    if term in filename_lower:\n        block(f\"Forbidden name: '{term}' indicates unclear responsibility\")\n```\n\n**Keine Ausnahmen.** Diese Begriffe sind niemals architektonisch sauber.\n\n---\n\n## Post-Hook (WARN)\n\n### W1.1: Klassengröße (LOC)\n\n**Regel:** Große Klassen haben oft mehrere Verantwortungen.\n\n| LOC | Aktion |\n|-----|--------|\n| > 200 | Hinweis |\n| > 300 | Warnung |\n\n**Warum zwei Schwellen:**\n- 200: \"Du näherst dich dem Limit\"\n- 300: \"Hier stimmt strukturell etwas nicht\"\n\n**Messung:** Nicht-leere Zeilen.\n\n---\n\n### W1.2: Public-Method-Count\n\n**Regel:** Viele öffentliche Methoden = viele Interaktionspunkte = breite Verantwortung.\n\n| Count | Aktion |\n|-------|--------|\n| > 10 | Warnung |\n\n**Warum 10:**\n- Ein fokussiertes Interface hat 3-7 Methoden\n- 10+ deutet auf Feature Creep\n\n---\n\n### W1.3: Constructor-Parameter\n\n**Regel:** Viele Dependencies = viele Gründe sich zu ändern.\n\n| Count | Aktion |\n|-------|--------|\n| > 5 | Warnung |\n\n**Warum 5:**\n- 1-3: Normal\n- 4-5: Grenzwertig\n- 6+: Fast immer strukturelles Problem\n\n---\n\n### W1.4: Dependency-Count (use-Statements)\n\n**Regel:** Viele Imports = Kopplung an viele Module = viele Änderungsgründe.\n\n| Count | Aktion |\n|-------|--------|\n| > 10 | Warnung |\n\n**Warum 10:**\n- PHP-interne Classes zählen mit\n- Framework-Imports zählen mit\n- 10+ externe Abhängigkeiten ist ein Warnsignal\n\n---\n\n### W1.5: Verdächtige Namen\n\n**Regel:** Manche Begriffe sind nicht eindeutig Müll, aber verdächtig.\n\n| Begriff | Aktion |\n|---------|--------|\n| Manager | Hinweis |\n| And (als Wort) | Hinweis |\n\n**Warum Hinweis statt Block:**\n- \"UserManager\" kann legitim sein\n- \"ReadAndWrite\" kann legitim sein (obwohl fragwürdig)\n- Menschliche Entscheidung erforderlich\n\n---\n\n## Gestrichen\n\n### Verschachtelungstiefe\n\n**Ursprüngliche Idee:** > 4 Ebenen = Warnung\n\n**Warum gestrichen:**\n- Indent-basierte Messung ist nicht zuverlässig\n- PHP erlaubt verschiedene Styles (2, 4, Tabs)\n- Fluent Interfaces erzeugen tiefe Indents ohne Kontrollfluss\n- Array-Definitionen erzeugen tiefe Indents\n- False Positives zu hoch\n\n**Alternative:** Erst implementieren wenn AST-Parsing verfügbar.\n\n---\n\n## Zusammenfassung\n\n### Pre-Hook\n\n| ID | Prüfung | Aktion |\n|----|---------|--------|\n| P1.1 | @responsibility fehlt oder Multi-Responsibility | BLOCK |\n| P1.2 | Müllhalden-Name (Helper, Utils, Common, Misc, Base) | BLOCK |\n\n### Post-Hook\n\n| ID | Prüfung | Schwelle | Aktion |\n|----|---------|----------|--------|\n| W1.1 | LOC | > 300 | WARN |\n| W1.2 | Public Methods | > 10 | WARN |\n| W1.3 | Constructor Params | > 5 | WARN |\n| W1.4 | Dependencies (use) | > 10 | WARN |\n| W1.5 | Verdächtige Namen | Manager, And | HINT |\n\n### Allowlist\n\n| Pfad | Grund |\n|------|-------|\n| \/vendor\/ | Third-party Code |\n| \/tests\/ | Test-Konventionen anders |\n| \/Test\/ | PHPUnit |\n\n---\n\n## Selbstkritik und Entscheidungen\n\n| Frage | Entscheidung | Begründung |\n|-------|--------------|------------|\n| Ist Multi-Word-Check im Header Over-Engineering? | Nein | Ein Regex. Verhindert \"Macht A und B\". Minimaler Aufwand, hoher Nutzen. |\n| Ist Dependency-Count SRP? | Ja | Viele Dependencies = viele Änderungsgründe = SRP-Verletzung per Definition. |\n| Sollen Factories in Allowlist? | Nein | Auch Factories sollten nicht 50 Methoden haben. Wenn zu groß, aufteilen. |\n| Warum keine Kontext-Schwellen für Repositories? | YAGNI | Komplexität ohne klaren Nutzen. Wer > 10 Methoden braucht, soll bewusst entscheiden. |\n\n---\n\n## Implementierung\n\n**Dateien:**\n- `\/var\/www\/tools\/ki-protokoll\/claude-hook\/srp_kiss_guard.py` (Pre-Hook)\n- `\/var\/www\/tools\/ki-protokoll\/claude-hook\/srp_kiss_metrics.py` (Post-Hook)\n\n**Hooks in settings.json:**\n```json\n{\n  \"PreToolUse\": [\n    {\n      \"matcher\": \"Write\",\n      \"hooks\": [\n        {\n          \"type\": \"command\",\n          \"command\": \"\/var\/www\/tools\/ki-protokoll\/claude-hook\/srp_kiss_guard.py\",\n          \"timeout\": 5\n        }\n      ]\n    }\n  ],\n  \"PostToolUse\": [\n    {\n      \"matcher\": \"Write|Edit\",\n      \"hooks\": [\n        {\n          \"type\": \"command\",\n          \"command\": \"\/var\/www\/tools\/ki-protokoll\/claude-hook\/srp_kiss_metrics.py\",\n          \"timeout\": 5\n        }\n      ]\n    }\n  ]\n}\n```\n\n---\n\n# Prüfung 2: MVC + CRUD\n\n## Ziel\n\n**MVC:** Klare Rollentrennung.\n- Controller: Orchestrierung (nimmt Request, ruft UseCase, gibt Response)\n- View: Darstellung (keine Logik)\n- Model: Geschäftslogik (Domain + UseCases)\n\n**CRUD:** Persistenz-Disziplin.\n- Create\/Read\/Update\/Delete gehören in Repository\n- Transaktionen gehören in UseCase oder Repository, nicht Controller\n- Controller orchestriert, persistiert nicht\n\n## Grundsatz\n\nMVC ist strukturell. Die Rollen sind durch Pfade definiert:\n- `\/Controller\/` = Presentation Layer\n- `\/View\/`, `\/templates\/` = Templates\n- `\/Domain\/` = Business Logic\n- `\/UseCases\/`, `\/Application\/` = Application Logic\n- `\/Infrastructure\/Persistence\/` = Repositories\n\n## Abgrenzung zu anderen Prüfungen\n\n| Aspekt | Prüfung | Begründung |\n|--------|---------|------------|\n| Klassen-Größe | 1 (SRP) | Strukturelle Metrik |\n| Methoden-Anzahl | 1 (SRP) | Strukturelle Metrik |\n| Abhängigkeits-Richtung | 6 (DIP) | Dependency Inversion |\n| **Rollen-Einhaltung** | **2 (MVC)** | Controller\/View\/Model Trennung |\n| **Persistenz-Ort** | **2 (CRUD)** | Wer darf DB anfassen |\n\n---\n\n## Pre-Hook (BLOCK)\n\n### P2.1: Keine SQL-Statements in Controller\n\n**Regel:** Controller dürfen keine direkten SQL-Queries enthalten.\n\n**Scope:** `\/Controller\/`\n\n**Pattern:**\n```python\nsql_patterns = [\n    r\"\\bSELECT\\s+.+\\s+FROM\\b\",\n    r\"\\bINSERT\\s+INTO\\b\",\n    r\"\\bUPDATE\\s+\\w+\\s+SET\\b\",\n    r\"\\bDELETE\\s+FROM\\b\",\n]\n```\n\n**Warum diese Pattern:**\n- Spezifisch genug um False Positives zu vermeiden\n- `SELECT your country` matcht nicht (kein FROM)\n- `INSERT INTO users` matcht (eindeutig SQL)\n\n**Unterschied zu Prüfung 1\/6:**\n- H-Regeln prüfen Objekte (new Repository, DatabaseFactory)\n- P2.1 prüft String-Literale (SQL-Statements)\n\n---\n\n### P2.2: Keine Transaktionen in Controller\n\n**Regel:** Transaktions-Kontrolle gehört nicht in Controller.\n\n**Scope:** `\/Controller\/`\n\n**Pattern:**\n```python\ntransaction_patterns = [\n    r\"\\bbeginTransaction\\s*\\(\",\n    r\"\\bcommit\\s*\\(\",\n    r\"\\brollBack\\s*\\(\",\n    r\"\\brollback\\s*\\(\",\n]\n```\n\n**Begründung:**\n- Transaktionen sind Infrastruktur-Concern\n- Controller soll Request→Response orchestrieren, nicht DB-Konsistenz managen\n- Transaktionen gehören in UseCase oder Repository\n\n---\n\n### P2.3: Kein direktes Rendering in Controller\n\n**Regel:** Controller erzeugen keine direkte Ausgabe.\n\n**Scope:** `\/Controller\/`\n\n**Pattern:**\n```python\noutput_patterns = [\n    r\"\\becho\\s\",\n    r\"\\becho\\(\",\n    r\"\\bprint\\s\",\n    r\"\\bprint\\(\",\n]\n```\n\n**Nicht blockiert:**\n- `print_r` (Debug)\n- `printf` (Formatierung, selten)\n- `return json_encode()` (Response-Objekt)\n\n**Begründung:**\n- Output gehört in View oder Response-Objekt\n- echo\/print in Controller = MVC-Verletzung\n- CLI-Code gehört in `\/Console\/` oder `\/Command\/`, nicht `\/Controller\/`\n\n---\n\n### P2.4: Keine DB-Artefakte in UseCases\n\n**Regel:** UseCases nutzen Repository-Interfaces, nicht direkte DB.\n\n**Scope:** `\/UseC... [TRUNCATED-3a3e9d813bfb694e]",
        "structuredPatch": [
            {
                "oldStart": 864,
                "oldLines": 6,
                "newStart": 864,
                "newLines": 261,
                "lines": [
                    " ",
                    " ---",
                    " ",
                    "-# Prüfung 4-6",
                    "+# Prüfung 4: OOP",
                    " ",
                    "-*Werden nach Abnahme von Prüfung 3 definiert.*",
                    "+## Ziel",
                    "+",
                    "+**OOP (Object-Oriented Programming):**",
                    "+- Verhalten und Zustand sind gekoppelt",
                    "+- Objekte sind keine reinen Datencontainer",
                    "+- Keine anämischen Modelle ohne Grund",
                    "+- Kapselung wird respektiert",
                    "+",
                    "+## Grundsatz",
                    "+",
                    "+OOP-Verletzungen sind schwerer zu erkennen als strukturelle Verstöße.",
                    "+Ein Objekt ist \"anämisch\" wenn es nur Getter\/Setter hat, aber keine Logik.",
                    "+Das ist semantisch. Regex kann das nicht sicher erkennen.",
                    "+",
                    "+Daher: **Hauptsächlich Post-Hook (Warnungen), wenige Pre-Hook (nur Eindeutiges).**",
                    "+",
                    "+## Abgrenzung zu anderen Prüfungen",
                    "+",
                    "+| Aspekt | Prüfung | Begründung |",
                    "+|--------|---------|------------|",
                    "+| Klassen-Größe | 1 (SRP) | Strukturelle Metrik |",
                    "+| Rollen-Einhaltung | 2 (MVC) | Controller\/Model-Trennung |",
                    "+| Typisierung | 3 (Types) | Parameter\/Return |",
                    "+| **Verhalten + Zustand** | **4 (OOP)** | Objektintegrität |",
                    "+| Duplikation | 5 (DRY) | Code-Wiederholung |",
                    "+| Abhängigkeiten | 6 (DIP) | Dependency Inversion |",
                    "+",
                    "+---",
                    "+",
                    "+## Pre-Hook (BLOCK)",
                    "+",
                    "+### P4.1: Keine public Properties in Domain",
                    "+",
                    "+**Regel:** Domain-Klassen dürfen keine public Properties haben.",
                    "+",
                    "+**Scope:** `\/Domain\/`",
                    "+",
                    "+**Pattern:**",
                    "+```python",
                    "+public_property = re.search(r\"public\\s+(?!function|static\\s+function)\\s*\\$\\w+\", content)",
                    "+# Oder einfacher:",
                    "+public_property = re.search(r\"public\\s+(?:readonly\\s+)?(?:\\?\\w+|\\w+)?\\s*\\$\\w+\", content)",
                    "+```",
                    "+",
                    "+**Vereinfachtes Pattern:**",
                    "+```python",
                    "+# public $var oder public Type $var oder public ?Type $var",
                    "+public_property = re.search(r\"public\\s+(?!function|const|static\\s+function).*\\$\\w+\", content)",
                    "+```",
                    "+",
                    "+**Warum Block:**",
                    "+- Public Properties brechen Kapselung",
                    "+- Domain-Objekte müssen ihren Zustand schützen",
                    "+- Zugriff nur über Methoden",
                    "+",
                    "+**Ausnahme:**",
                    "+- `public readonly` (PHP 8.1+) ist akzeptabel",
                    "+- DTOs in `\/Infrastructure\/` dürfen public Properties haben",
                    "+",
                    "+**Verfeinertes Pattern:**",
                    "+```python",
                    "+# Block public Properties, aber erlaube public readonly",
                    "+if \"\/Domain\/\" in file_path:",
                    "+    public_props = re.findall(r\"public\\s+(?!readonly|function|const|static).*\\$\\w+\", content)",
                    "+    if public_props:",
                    "+        block(\"Public property in Domain. Use getter methods or public readonly.\")",
                    "+```",
                    "+",
                    "+---",
                    "+",
                    "+### P4.2: Keine direkten Property-Änderungen von außen (gestrichen)",
                    "+",
                    "+**Ursprüngliche Idee:** Prüfen ob Properties direkt geändert werden.",
                    "+",
                    "+**Warum gestrichen:**",
                    "+- Nicht prüfbar ohne Kontrollfluss-Analyse",
                    "+- `$obj->property = $value` vs `$this->property = $value`",
                    "+- Erfordert Wissen über Kontext",
                    "+",
                    "+**Entscheidung:** Streichen. P4.1 (keine public properties) deckt das ab.",
                    "+",
                    "+---",
                    "+",
                    "+## Post-Hook (WARN)",
                    "+",
                    "+### W4.1: Anämisches Model (Getter\/Setter-Ratio)",
                    "+",
                    "+**Regel:** Warnung wenn eine Klasse fast nur Getter\/Setter hat.",
                    "+",
                    "+**Heuristik:**",
                    "+```python",
                    "+getters = len(re.findall(r\"public\\s+function\\s+get[A-Z]\\w*\\s*\\(\", content))",
                    "+setters = len(re.findall(r\"public\\s+function\\s+set[A-Z]\\w*\\s*\\(\", content))",
                    "+all_public = len(re.findall(r\"public\\s+function\\s+\\w+\", content))",
                    "+",
                    "+if all_public > 0:",
                    "+    accessor_ratio = (getters + setters) \/ all_public",
                    "+    if accessor_ratio > 0.7 and all_public > 4:",
                    "+        warn(f\"Potential anemic model: {int(accessor_ratio*100)}% accessors\")",
                    "+```",
                    "+",
                    "+**Schwelle:** > 70% Getter\/Setter bei > 4 public methods",
                    "+",
                    "+**Warum Warnung:**",
                    "+- Anämische Models sind nicht immer falsch (DTOs)",
                    "+- Aber: Domain-Objekte sollten Verhalten haben",
                    "+- Menschliche Entscheidung erforderlich",
                    "+",
                    "+**Scope:** Alle Klassen, aber besonders relevant für `\/Domain\/`",
                    "+",
                    "+---",
                    "+",
                    "+### W4.2: Klasse ohne Verhalten",
                    "+",
                    "+**Regel:** Warnung wenn Klasse Properties hat aber keine echten Methoden.",
                    "+",
                    "+**Heuristik:**",
                    "+```python",
                    "+has_properties = bool(re.search(r\"(?:private|protected|public)\\s+.*\\$\\w+\", content))",
                    "+methods = re.findall(r\"(?:public|private|protected)\\s+function\\s+(\\w+)\", content)",
                    "+real_methods = [m for m in methods if not m.startswith((\"get\", \"set\", \"__\"))]",
                    "+",
                    "+if has_properties and len(real_methods) == 0:",
                    "+    warn(\"Class has properties but no behavior methods\")",
                    "+```",
                    "+",
                    "+**Warum Warnung:**",
                    "+- Klassen mit nur Properties + Accessors sind verdächtig",
                    "+- Aber: Manche DTOs sind legitim",
                    "+- Kontext entscheidet",
                    "+",
                    "+---",
                    "+",
                    "+### W4.3: Fehlende Kapselung (protected\/private Ratio)",
                    "+",
                    "+**Regel:** Warnung wenn zu viele Properties public oder protected sind.",
                    "+",
                    "+**Heuristik:**",
                    "+```python",
                    "+public_props = len(re.findall(r\"public\\s+(?!function|const).*\\$\", content))",
                    "+protected_props = len(re.findall(r\"protected\\s+(?!function|const).*\\$\", content))",
                    "+private_props = len(re.findall(r\"private\\s+(?!function|const).*\\$\", content))",
                    "+",
                    "+total = public_props + protected_props + private_props",
                    "+if total > 0:",
                    "+    exposed_ratio = (public_props + protected_props) \/ total",
                    "+    if exposed_ratio > 0.5 and total > 3:",
                    "+        warn(f\"Low encapsulation: {int(exposed_ratio*100)}% exposed properties\")",
                    "+```",
                    "+",
                    "+**Warum Warnung:**",
                    "+- Viele protected Properties deuten auf Vererbungs-Missbrauch",
                    "+- Gute OOP bevorzugt Komposition über Vererbung",
                    "+- Aber: Nicht immer falsch",
                    "+",
                    "+---",
                    "+",
                    "+### W4.4: Zu viele statische Methoden",
                    "+",
                    "+**Regel:** Warnung bei vielen static methods.",
                    "+",
                    "+**Heuristik:**",
                    "+```python",
                    "+static_methods = len(re.findall(r\"public\\s+static\\s+function\", content))",
                    "+all_methods = len(re.findall(r\"public\\s+function\", content))",
                    "+",
                    "+if all_methods > 0:",
                    "+    static_ratio = static_methods \/ all_methods",
                    "+    if static_ratio > 0.5 and static_methods > 2:",
                    "+        warn(f\"High static method ratio: {static_methods}\/{all_methods}\")",
                    "+```",
                    "+",
                    "+**Warum Warnung:**",
                    "+- Statische Methoden sind prozedural, nicht OO",
                    "+- Erschweren Testing",
                    "+- Aber: Factory-Pattern, Utility-Functions legitim",
                    "+",
                    "+---",
                    "+",
                    "+## Gestrichen",
                    "+",
                    "+### Property-Änderungs-Tracking",
                    "+",
                    "+**Warum gestrichen:** Ohne Kontrollfluss-Analyse nicht möglich.",
                    "+",
                    "+### Interface-Segregation-Prüfung",
                    "+",
                    "+**Warum gestrichen:** ISP gehört zu SOLID (Prüfung 6), nicht OOP.",
                    "+",
                    "+### Inheritance-Depth-Prüfung",
                    "+",
                    "+**Ursprüngliche Idee:** Warnung bei tiefer Vererbungshierarchie.",
                    "+",
                    "+**Warum gestrichen:**",
                    "+- Erfordert Analyse über Dateigrenzen hinweg",
                    "+- Single-File-Hooks können das nicht",
                    "+- PHPStan kann das",
                    "+",
                    "+---",
                    "+",
                    "+## Zusammenfassung",
                    "+",
                    "+### Pre-Hook",
                    "+",
                    "+| ID | Prüfung | Scope | Aktion |",
                    "+|----|---------|-------|--------|",
                    "+| P4.1 | Public Property in Domain | \/Domain\/ | BLOCK |",
                    "+",
                    "+### Post-Hook",
                    "+",
                    "+| ID | Prüfung | Schwelle | Aktion |",
                    "+|----|---------|----------|--------|",
                    "+| W4.1 | Anämisches Model | > 70% Accessors, > 4 methods | WARN |",
                    "+| W4.2 | Klasse ohne Verhalten | 0 real methods | WARN |",
                    "+| W4.3 | Niedrige Kapselung | > 50% exposed, > 3 props | WARN |",
                    "+| W4.4 | Hohe Static-Ratio | > 50%, > 2 static | WARN |",
                    "+",
                    "+### Allowlist",
                    "+",
                    "+| Pfad | Grund |",
                    "+|------|-------|",
                    "+| \/vendor\/ | Third-party |",
                    "+| \/tests\/ | Test-Code |",
                    "+| \/Test\/ | PHPUnit |",
                    "+| \/Infrastructure\/DTO\/ | DTOs dürfen anämisch sein |",
                    "+",
                    "+---",
                    "+",
                    "+## Selbstkritik und Entscheidungen",
                    "+",
                    "+| Frage | Entscheidung | Begründung |",
                    "+|-------|--------------|------------|",
                    "+| Warum nur ein Pre-Hook? | OOP ist semantisch | Regex kann Verhalten nicht erkennen |",
                    "+| Warum public readonly erlauben? | PHP 8.1 Feature | Immutability ist gutes OOP |",
                    "+| DTOs in Allowlist? | Ja | DTOs sind per Definition anämisch |",
                    "+| Inheritance-Depth fehlt? | Ja, bewusst | Erfordert Cross-File-Analyse |",
                    "+",
                    "+---",
                    "+",
                    "+## OOP-Prinzipien vs. Prüfbarkeit",
                    "+",
                    "+| Prinzip | Prüfbar? | Methode |",
                    "+|---------|----------|---------|",
                    "+| Kapselung | Teilweise | Property-Visibility |",
                    "+| Verhalten+Zustand | Heuristisch | Accessor-Ratio |",
                    "+| Tell, Don't Ask | Nein | Semantisch |",
                    "+| Law of Demeter | Nein | Kontrollfluss |",
                    "+| Favor Composition | Teilweise | Static\/Inheritance |",
                    "+",
                    "+**Fazit:** OOP-Qualität ist nur indirekt messbar. Hooks erkennen Symptome, nicht Ursachen.",
                    "+",
                    "+---",
                    "+",
                    "+# Prüfung 5-6",
                    "+",
                    "+*Werden nach Abnahme von Prüfung 4 definiert.*"
                ]
            }
        ],
        "userModified": false,
        "replaceAll": false
    }
}
← Vorheriger Zur Liste Nächster →