pre_rules_mvc.py

Code Hygiene Score: 100

Keine Issues gefunden.

Dependencies 5

Funktionen 4

Code

#!/usr/bin/env python3
"""
Pre-Hook MVC Regeln (BLOCK) - Controller-Reinheit.

P2.x Regeln: MVC-Trennung, keine SQL/Transaktionen/Output in Controller
"""

import re
from typing import Optional
from .rule_base import GLOBAL_ALLOWLIST, is_in_allowlist, block


# =============================================================================
# PRÜFUNG 2: MVC + CRUD
# =============================================================================

def p2_1_no_sql_in_controller(file_path: str, content: str) -> Optional[dict]:
    """P2.1: Keine SQL-Statements in Controller."""
    if "/Controller/" not in file_path:
        return None
    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):
        return None

    sql_patterns = [
        r"\bSELECT\s+.+\s+FROM\b",
        r"\bINSERT\s+INTO\b",
        r"\bUPDATE\s+\w+\s+SET\b",
        r"\bDELETE\s+FROM\b",
    ]

    for pattern in sql_patterns:
        if re.search(pattern, content, re.IGNORECASE):
            return block("P2.1", "SQL statement in Controller. Move to Repository.")

    return None


def p2_2_no_transactions_in_controller(file_path: str, content: str) -> Optional[dict]:
    """P2.2: Keine Transaktionen in Controller."""
    if "/Controller/" not in file_path:
        return None
    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):
        return None

    transaction_patterns = [
        r"\bbeginTransaction\s*\(",
        r"\bcommit\s*\(",
        r"\brollBack\s*\(",
        r"\brollback\s*\(",
    ]

    for pattern in transaction_patterns:
        if re.search(pattern, content):
            return block("P2.2", "Transaction control in Controller. Move to UseCase or Repository.")

    return None


def p2_3_no_echo_in_controller(file_path: str, content: str) -> Optional[dict]:
    """P2.3: Kein echo/print in Controller."""
    if "/Controller/" not in file_path:
        return None
    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):
        return None

    output_patterns = [
        r"\becho\s+",
        r"\becho\(",
        r"\bprint\s+",
        r"\bprint\(",
    ]

    for pattern in output_patterns:
        if re.search(pattern, content):
            return block("P2.3", "Direct output (echo/print) in Controller. Use Response object.")

    return None


def p2_4_no_db_in_usecases(file_path: str, content: str) -> Optional[dict]:
    """P2.4: Keine DB-Artefakte in UseCases."""
    if "/UseCases/" not in file_path and "/Application/" not in file_path:
        return None
    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):
        return None

    db_patterns = [
        r"\bnew\s+PDO\b",
        r"\bPDO::",
        r"\bDatabaseFactory::",
        r"\bSELECT\s+.+\s+FROM\b",
        r"\bINSERT\s+INTO\b",
        r"\bUPDATE\s+\w+\s+SET\b",
        r"\bDELETE\s+FROM\b",
    ]

    for pattern in db_patterns:
        if re.search(pattern, content, re.IGNORECASE):
            return block("P2.4", "DB artifact in UseCase/Application. Use Repository interface.")

    return None


# =============================================================================
# RULE COLLECTION
# =============================================================================

RULES = [
    p2_1_no_sql_in_controller,
    p2_2_no_transactions_in_controller,
    p2_3_no_echo_in_controller,
    p2_4_no_db_in_usecases,
]
← Übersicht