Protokoll #15642
| ID | 15642 |
|---|---|
| Zeitstempel | 2025-12-25 16:56:09.785303 |
| Client | root |
| 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 |
| Modell | claude-sonnet-4-20250514 |
| Status | completed |
| Tokens | 4,441 (Input: 0, Output: 0) |
| Dauer | 112 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": "Write",
"tool_input": {
"file_path": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/rules_quality.py",
"content": "#!\/usr\/bin\/env python3\n\"\"\"\nQuality Rules (W1, W4, W6) - SRP, KISS, OOP, SOLID.\n\nRegeln für Code-Qualität: Klassengröße, Single Responsibility,\nOOP-Prinzipien, Encapsulation, Interface Segregation.\n\"\"\"\n\nimport re\nfrom pathlib import Path\nfrom typing import List\nfrom .rule_base import Rule, DTO_ALLOWLIST\n\n\n# =============================================================================\n# W1: SRP + KISS - Single Responsibility & Keep It Simple\n# =============================================================================\n\nclass W1_1_ClassSize(Rule):\n \"\"\"W1.1: Klassengröße (max 300 LOC).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n from .rule_base import count_non_empty_lines\n loc = count_non_empty_lines(content)\n warnings = []\n\n if loc > 300:\n warnings.append(f\"W1.1: Class has {loc} lines (max 300). Consider splitting.\")\n elif loc > 200:\n warnings.append(f\"W1.1: Class has {loc} lines (approaching limit of 300).\")\n\n return warnings\n\n\nclass W1_2_PublicMethodCount(Rule):\n \"\"\"W1.2: Anzahl public methods (max 10).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n public_methods = re.findall(r\"public\\s+function\\s+\\w+\", content)\n count = len(public_methods)\n\n if count > 10:\n return [f\"W1.2: Class has {count} public methods (max 10). Consider splitting.\"]\n\n return []\n\n\nclass W1_3_ConstructorParams(Rule):\n \"\"\"W1.3: Constructor-Parameter (max 5).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n constructor_match = re.search(r\"function\\s+__construct\\s*\\(([^)]*)\\)\", content, re.DOTALL)\n if not constructor_match:\n return []\n\n params = constructor_match.group(1)\n param_count = len([p for p in params.split(',') if p.strip()])\n\n if param_count > 5:\n return [f\"W1.3: Constructor has {param_count} parameters (max 5). Consider refactoring.\"]\n\n return []\n\n\nclass W1_4_DependencyCount(Rule):\n \"\"\"W1.4: Anzahl use-Statements (max 10).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n use_statements = re.findall(r\"^use\\s+\", content, re.MULTILINE)\n count = len(use_statements)\n\n if count > 10:\n return [f\"W1.4: Class has {count} dependencies (max 10). Consider reducing coupling.\"]\n\n return []\n\n\nclass W1_5_SuspiciousNames(Rule):\n \"\"\"W1.5: Verdächtige Namen (Manager, And).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n filename = Path(file_path).stem\n hints = []\n\n if \"Manager\" in filename:\n hints.append(f\"W1.5: Hint - '{filename}' contains 'Manager'. Verify single responsibility.\")\n\n if re.search(r\"[a-z]And[A-Z]\", filename):\n hints.append(f\"W1.5: Hint - '{filename}' contains 'And'. May indicate multiple responsibilities.\")\n\n return hints\n\n\n# =============================================================================\n# W4: OOP - Object-Oriented Programming Principles\n# =============================================================================\n\nclass W4_1_AnemicModel(Rule):\n \"\"\"W4.1: Anämisches Model (hohe Accessor-Ratio > 70%).\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=DTO_ALLOWLIST)\n\n def check(self, file_path: str, content: str) -> List[str]:\n getters = len(re.findall(r\"public\\s+function\\s+get[A-Z]\\w*\\s*\\(\", content))\n setters = len(re.findall(r\"public\\s+function\\s+set[A-Z]\\w*\\s*\\(\", content))\n all_public = len(re.findall(r\"public\\s+function\\s+\\w+\", content))\n\n if all_public > 4:\n accessor_ratio = (getters + setters) \/ all_public\n if accessor_ratio > 0.7:\n return [f\"W4.1: Potential anemic model: {int(accessor_ratio*100)}% accessors. Consider adding behavior.\"]\n\n return []\n\n\nclass W4_2_ClassWithoutBehavior(Rule):\n \"\"\"W4.2: Klasse ohne Verhalten.\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=DTO_ALLOWLIST)\n\n def check(self, file_path: str, content: str) -> List[str]:\n has_properties = bool(re.search(r\"(?:private|protected|public)\\s+.*\\$\\w+\", content))\n methods = re.findall(r\"(?:public|private|protected)\\s+function\\s+(\\w+)\", content)\n real_methods = [m for m in methods if not m.startswith((\"get\", \"set\", \"__\"))]\n\n if has_properties and len(real_methods) == 0:\n return [\"W4.2: Class has properties but no behavior methods.\"]\n\n return []\n\n\nclass W4_3_LowEncapsulation(Rule):\n \"\"\"W4.3: Niedrige Kapselung (> 50% exposed properties).\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=DTO_ALLOWLIST)\n\n def check(self, file_path: str, content: str) -> List[str]:\n public_props = len(re.findall(r\"public\\s+(?!function|const).*\\$\", content))\n protected_props = len(re.findall(r\"protected\\s+(?!function|const).*\\$\", content))\n private_props = len(re.findall(r\"private\\s+(?!function|const).*\\$\", content))\n\n total = public_props + protected_props + private_props\n if total > 3:\n exposed_ratio = (public_props + protected_props) \/ total\n if exposed_ratio > 0.5:\n return [f\"W4.3: Low encapsulation: {int(exposed_ratio*100)}% exposed properties.\"]\n\n return []\n\n\nclass W4_4_HighStaticRatio(Rule):\n \"\"\"W4.4: Hohe Static-Ratio (> 50% static methods).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n static_methods = len(re.findall(r\"public\\s+static\\s+function\", content))\n all_methods = len(re.findall(r\"public\\s+function\", content))\n\n if all_methods > 0 and static_methods > 2:\n static_ratio = static_methods \/ all_methods\n if static_ratio > 0.5:\n return [f\"W4.4: High static method ratio: {static_methods}\/{all_methods}. Consider instance methods.\"]\n\n return []\n\n\n# =============================================================================\n# W6: SOLID + DIP - Interface Segregation Principle\n# =============================================================================\n\nclass W6_1_InterfaceTooLarge(Rule):\n \"\"\"W6.1: Interface mit zu vielen Methoden (max 7).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n interface_match = re.search(r\"interface\\s+(\\w+)[^{]*\\{\", content)\n if not interface_match:\n return []\n\n interface_name = interface_match.group(1)\n interface_start = interface_match.end()\n interface_content = content[interface_start:]\n\n # Finde Interface-Ende\n brace_count = 1\n end_pos = 0\n for i, char in enumerate(interface_content):\n if char == '{':\n brace_count += 1\n elif char == '}':\n brace_count -= 1\n if brace_count == 0:\n end_pos = i\n break\n\n interface_body = interface_content[:end_pos]\n method_count = len(re.findall(r\"function\\s+\\w+\", interface_body))\n\n if method_count > 7:\n return [f\"W6.1: Interface '{interface_name}' has {method_count} methods (max 7). Consider splitting (ISP).\"]\n\n return []\n\n\nclass W6_2_TooManyInterfaces(Rule):\n \"\"\"W6.2: Klasse implementiert zu viele Interfaces (max 5).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n implements_match = re.search(r\"implements\\s+([^{]+)\", content)\n if not implements_match:\n return []\n\n interfaces = [i.strip() for i in implements_match.group(1).split(',') if i.strip()]\n count = len(interfaces)\n\n if count > 5:\n return [f\"W6.2: Class implements {count} interfaces (max 5). Potential role confusion.\"]\n\n return []\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n W1_1_ClassSize(),\n W1_2_PublicMethodCount(),\n W1_3_ConstructorParams(),\n W1_4_DependencyCount(),\n W1_5_SuspiciousNames(),\n W4_1_AnemicModel(),\n W4_2_ClassWithoutBehavior(),\n W4_3_LowEncapsulation(),\n W4_4_HighStaticRatio(),\n W6_1_InterfaceTooLarge(),\n W6_2_TooManyInterfaces(),\n]\n"
}
}
Response
{
"tool_response": {
"type": "create",
"filePath": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/rules_quality.py",
"content": "#!\/usr\/bin\/env python3\n\"\"\"\nQuality Rules (W1, W4, W6) - SRP, KISS, OOP, SOLID.\n\nRegeln für Code-Qualität: Klassengröße, Single Responsibility,\nOOP-Prinzipien, Encapsulation, Interface Segregation.\n\"\"\"\n\nimport re\nfrom pathlib import Path\nfrom typing import List\nfrom .rule_base import Rule, DTO_ALLOWLIST\n\n\n# =============================================================================\n# W1: SRP + KISS - Single Responsibility & Keep It Simple\n# =============================================================================\n\nclass W1_1_ClassSize(Rule):\n \"\"\"W1.1: Klassengröße (max 300 LOC).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n from .rule_base import count_non_empty_lines\n loc = count_non_empty_lines(content)\n warnings = []\n\n if loc > 300:\n warnings.append(f\"W1.1: Class has {loc} lines (max 300). Consider splitting.\")\n elif loc > 200:\n warnings.append(f\"W1.1: Class has {loc} lines (approaching limit of 300).\")\n\n return warnings\n\n\nclass W1_2_PublicMethodCount(Rule):\n \"\"\"W1.2: Anzahl public methods (max 10).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n public_methods = re.findall(r\"public\\s+function\\s+\\w+\", content)\n count = len(public_methods)\n\n if count > 10:\n return [f\"W1.2: Class has {count} public methods (max 10). Consider splitting.\"]\n\n return []\n\n\nclass W1_3_ConstructorParams(Rule):\n \"\"\"W1.3: Constructor-Parameter (max 5).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n constructor_match = re.search(r\"function\\s+__construct\\s*\\(([^)]*)\\)\", content, re.DOTALL)\n if not constructor_match:\n return []\n\n params = constructor_match.group(1)\n param_count = len([p for p in params.split(',') if p.strip()])\n\n if param_count > 5:\n return [f\"W1.3: Constructor has {param_count} parameters (max 5). Consider refactoring.\"]\n\n return []\n\n\nclass W1_4_DependencyCount(Rule):\n \"\"\"W1.4: Anzahl use-Statements (max 10).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n use_statements = re.findall(r\"^use\\s+\", content, re.MULTILINE)\n count = len(use_statements)\n\n if count > 10:\n return [f\"W1.4: Class has {count} dependencies (max 10). Consider reducing coupling.\"]\n\n return []\n\n\nclass W1_5_SuspiciousNames(Rule):\n \"\"\"W1.5: Verdächtige Namen (Manager, And).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n filename = Path(file_path).stem\n hints = []\n\n if \"Manager\" in filename:\n hints.append(f\"W1.5: Hint - '{filename}' contains 'Manager'. Verify single responsibility.\")\n\n if re.search(r\"[a-z]And[A-Z]\", filename):\n hints.append(f\"W1.5: Hint - '{filename}' contains 'And'. May indicate multiple responsibilities.\")\n\n return hints\n\n\n# =============================================================================\n# W4: OOP - Object-Oriented Programming Principles\n# =============================================================================\n\nclass W4_1_AnemicModel(Rule):\n \"\"\"W4.1: Anämisches Model (hohe Accessor-Ratio > 70%).\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=DTO_ALLOWLIST)\n\n def check(self, file_path: str, content: str) -> List[str]:\n getters = len(re.findall(r\"public\\s+function\\s+get[A-Z]\\w*\\s*\\(\", content))\n setters = len(re.findall(r\"public\\s+function\\s+set[A-Z]\\w*\\s*\\(\", content))\n all_public = len(re.findall(r\"public\\s+function\\s+\\w+\", content))\n\n if all_public > 4:\n accessor_ratio = (getters + setters) \/ all_public\n if accessor_ratio > 0.7:\n return [f\"W4.1: Potential anemic model: {int(accessor_ratio*100)}% accessors. Consider adding behavior.\"]\n\n return []\n\n\nclass W4_2_ClassWithoutBehavior(Rule):\n \"\"\"W4.2: Klasse ohne Verhalten.\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=DTO_ALLOWLIST)\n\n def check(self, file_path: str, content: str) -> List[str]:\n has_properties = bool(re.search(r\"(?:private|protected|public)\\s+.*\\$\\w+\", content))\n methods = re.findall(r\"(?:public|private|protected)\\s+function\\s+(\\w+)\", content)\n real_methods = [m for m in methods if not m.startswith((\"get\", \"set\", \"__\"))]\n\n if has_properties and len(real_methods) == 0:\n return [\"W4.2: Class has properties but no behavior methods.\"]\n\n return []\n\n\nclass W4_3_LowEncapsulation(Rule):\n \"\"\"W4.3: Niedrige Kapselung (> 50% exposed properties).\"\"\"\n\n def __init__(self):\n super().__init__(allowlist=DTO_ALLOWLIST)\n\n def check(self, file_path: str, content: str) -> List[str]:\n public_props = len(re.findall(r\"public\\s+(?!function|const).*\\$\", content))\n protected_props = len(re.findall(r\"protected\\s+(?!function|const).*\\$\", content))\n private_props = len(re.findall(r\"private\\s+(?!function|const).*\\$\", content))\n\n total = public_props + protected_props + private_props\n if total > 3:\n exposed_ratio = (public_props + protected_props) \/ total\n if exposed_ratio > 0.5:\n return [f\"W4.3: Low encapsulation: {int(exposed_ratio*100)}% exposed properties.\"]\n\n return []\n\n\nclass W4_4_HighStaticRatio(Rule):\n \"\"\"W4.4: Hohe Static-Ratio (> 50% static methods).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n static_methods = len(re.findall(r\"public\\s+static\\s+function\", content))\n all_methods = len(re.findall(r\"public\\s+function\", content))\n\n if all_methods > 0 and static_methods > 2:\n static_ratio = static_methods \/ all_methods\n if static_ratio > 0.5:\n return [f\"W4.4: High static method ratio: {static_methods}\/{all_methods}. Consider instance methods.\"]\n\n return []\n\n\n# =============================================================================\n# W6: SOLID + DIP - Interface Segregation Principle\n# =============================================================================\n\nclass W6_1_InterfaceTooLarge(Rule):\n \"\"\"W6.1: Interface mit zu vielen Methoden (max 7).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n interface_match = re.search(r\"interface\\s+(\\w+)[^{]*\\{\", content)\n if not interface_match:\n return []\n\n interface_name = interface_match.group(1)\n interface_start = interface_match.end()\n interface_content = content[interface_start:]\n\n # Finde Interface-Ende\n brace_count = 1\n end_pos = 0\n for i, char in enumerate(interface_content):\n if char == '{':\n brace_count += 1\n elif char == '}':\n brace_count -= 1\n if brace_count == 0:\n end_pos = i\n break\n\n interface_body = interface_content[:end_pos]\n method_count = len(re.findall(r\"function\\s+\\w+\", interface_body))\n\n if method_count > 7:\n return [f\"W6.1: Interface '{interface_name}' has {method_count} methods (max 7). Consider splitting (ISP).\"]\n\n return []\n\n\nclass W6_2_TooManyInterfaces(Rule):\n \"\"\"W6.2: Klasse implementiert zu viele Interfaces (max 5).\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n implements_match = re.search(r\"implements\\s+([^{]+)\", content)\n if not implements_match:\n return []\n\n interfaces = [i.strip() for i in implements_match.group(1).split(',') if i.strip()]\n count = len(interfaces)\n\n if count > 5:\n return [f\"W6.2: Class implements {count} interfaces (max 5). Potential role confusion.\"]\n\n return []\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n W1_1_ClassSize(),\n W1_2_PublicMethodCount(),\n W1_3_ConstructorParams(),\n W1_4_DependencyCount(),\n W1_5_SuspiciousNames(),\n W4_1_AnemicModel(),\n W4_2_ClassWithoutBehavior(),\n W4_3_LowEncapsulation(),\n W4_4_HighStaticRatio(),\n W6_1_InterfaceTooLarge(),\n W6_2_TooManyInterfaces(),\n]\n",
"structuredPatch": [],
"originalFile": null
}
}